Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli/rad: issue list --output json
✗ CI failure WillForan committed 7 months ago
commit 28da0f8809026f070fc719dcf7e8c9d2d9ba9392
parent f1c7c9860716e5db88f657c61d39d6081fb5645f
1 failed 1 pending (2 total) View logs
2 files changed +65 -8
modified crates/radicle-cli/src/commands/issue.rs
@@ -8,6 +8,7 @@ use radicle::cob::common::Label;
use radicle::cob::issue::{CloseReason, State};
use radicle::cob::{issue, Title};

+
use radicle::cob::Timestamp;
use radicle::crypto;
use radicle::issue::cache::Issues as _;
use radicle::node::device::Device;
@@ -18,10 +19,12 @@ use radicle::storage;
use radicle::storage::{WriteRepository, WriteStorage};
use radicle::Profile;
use radicle::{cob, Node};
+
use radicle_cob::ObjectId;

pub use args::Args;
use args::{Assigned, Command, CommentAction, StateArg};

+
use crate::commands::issue::args::OutputFormat;
use crate::git::Rev;
use crate::node;
use crate::terminal as term;
@@ -201,6 +204,7 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
                issues,
                &list_args.assigned,
                &((&list_args.state).into()),
+
                &list_args.output_format.unwrap_or_default(),
                &profile,
                args.verbose,
            )?;
@@ -240,10 +244,23 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    Ok(())
}

+
#[derive(serde::Serialize)]
+
pub struct IssueSummary {
+
    id: ObjectId,
+
    state: State,
+
    title: String,
+
    author: String,
+
    did: String,
+
    labels: Vec<String>,
+
    assignees: Vec<String>,
+
    opened: Timestamp,
+
}
+

fn list<C>(
    cache: C,
    assigned: &Option<Assigned>,
    state: &Option<State>,
+
    output: &OutputFormat,
    profile: &profile::Profile,
    verbose: bool,
) -> anyhow::Result<()>
@@ -261,7 +278,7 @@ where
        None => None,
    };

-
    let mut all = cache
+
    let mut issues = cache
        .list()?
        .filter_map(|result| {
            let (id, issue) = match result {
@@ -289,13 +306,29 @@ where
        })
        .collect::<Vec<_>>();

-
    all.sort_by(|(id1, i1), (id2, i2)| {
-
        let by_timestamp = i2.timestamp().cmp(&i1.timestamp());
-
        let by_id = id1.cmp(id2);
+
    match output {
+
        OutputFormat::Pretty => {
+
            issues.sort_by(|(id1, i1), (id2, i2)| {
+
                let by_timestamp = i2.timestamp().cmp(&i1.timestamp());
+
                let by_id = id1.cmp(id2);

-
        by_timestamp.then(by_id)
-
    });
+
                by_timestamp.then(by_id)
+
            });
+
            print_issues_pretty(issues, &profile, verbose);
+
        }
+
        OutputFormat::Json => {
+
            print_issues_json(issues)?;
+
        }
+
    }

+
    Ok(())
+
}
+

+
fn print_issues_pretty(
+
    issues: Vec<(issue::IssueId, issue::Issue)>,
+
    profile: &profile::Profile,
+
    verbose: bool,
+
) {
    let mut table = term::Table::new(term::table::TableOptions::bordered());
    table.header([
        term::format::dim(String::from("●")).into(),
@@ -309,7 +342,7 @@ where
    ]);
    table.divider();

-
    table.extend(all.into_iter().map(|(id, issue)| {
+
    table.extend(issues.into_iter().map(|(id, issue)| {
        let assigned: String = issue
            .assignees()
            .map(|did| {
@@ -330,6 +363,10 @@ where
    }));

    table.print();
+
}
+

+
fn print_issues_json(issues: Vec<(issue::IssueId, issue::Issue)>) -> anyhow::Result<()> {
+
    println!("{}", serde_json::to_string_pretty(&issues)?);

    Ok(())
}
modified crates/radicle-cli/src/commands/issue/args.rs
@@ -1,6 +1,6 @@
use std::str::FromStr;

-
use clap::{Parser, Subcommand};
+
use clap::{Parser, Subcommand, ValueEnum};

use radicle::{
    cob::{Label, Reaction, Title},
@@ -212,6 +212,11 @@ pub(crate) struct EmptyArgs {

    #[clap(flatten)]
    pub(crate) state: EmptyStateArgs,
+

+
    #[arg(long = "output-format")]
+
    #[arg(value_name = "NAME")]
+
    #[arg(hide = true)]
+
    pub(crate) output_format: Option<OutputFormat>,
}

/// Counterpart to [`ListStateArgs`] for the empty subcommand.
@@ -242,6 +247,10 @@ pub(crate) struct ListArgs {

    #[clap(flatten)]
    pub(crate) state: ListStateArgs,
+

+
    #[arg(long = "output-format")]
+
    #[arg(value_name = "NAME")]
+
    pub(crate) output_format: Option<OutputFormat>,
}

#[derive(Parser, Debug, Default)]
@@ -296,10 +305,21 @@ impl From<EmptyArgs> for ListArgs {
        Self {
            assigned: args.assigned,
            state: ListStateArgs::from(args.state),
+
            output_format: args.output_format,
        }
    }
}

+
/// Argument value for list output.
+
#[derive(Clone, Copy, Debug, Default, ValueEnum)]
+
pub(crate) enum OutputFormat {
+
    /// Print formatted table (default)
+
    #[default]
+
    Pretty,
+
    /// Print list as JSON
+
    Json,
+
}
+

/// Arguments for the [`Command::Comment`] subcommand.
#[derive(Parser, Debug)]
pub(crate) struct CommentArgs {