Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli/issue: Allow list arguments for empty subcommand
✗ CI failure Erik Kundt committed 7 months ago
commit 71ffded74071755ff6855c2d6c8865a9576ee011
parent 93f0ce12ca166e4d31a535f79bf669dabef373e5
1 failed (1 total) View logs
3 files changed +102 -19
modified crates/radicle-cli/examples/rad-issue-list.md
@@ -49,3 +49,14 @@ $ rad issue list --solved
│ ●   d87dcfe   flux capacitor underpowered   alice    (you)   good-first-issue   alice       now    │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
+

+
Note: You can achieve the same by omitting the `list` subcommand, since that's the fallback when no subcommand is specified.
+

+
```
+
$ rad issue --solved
+
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
+
│ ●   ID        Title                         Author           Labels             Assignees   Opened │
+
├────────────────────────────────────────────────────────────────────────────────────────────────────┤
+
│ ●   d87dcfe   flux capacitor underpowered   alice    (you)   good-first-issue   alice       now    │
+
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
+
```
modified crates/radicle-cli/src/commands/issue.rs
@@ -41,7 +41,12 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    };
    let repo = profile.storage.repository_mut(rid)?;

-
    let command = args.command.unwrap_or_default();
+
    // Fallback to [`Command::List`] if no subcommand is provided. Construct it
+
    // with the [`EmptyArgs`] provided, if any.
+
    let command = args
+
        .command
+
        .unwrap_or_else(|| Command::List(args.empty.into()));
+

    let announce = !args.no_announce && command.should_announce_for();
    let mut issues = term::cob::issues_mut(&profile, &repo)?;

@@ -192,9 +197,14 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
            let signer = term::signer(&profile)?;
            issue.label(labels, &signer)?;
        }
-
        Command::List(list_args) => {
-
            let assigned = list_args.assigned.clone();
-
            list(issues, &assigned, &list_args.into(), &profile, args.verbose)?;
+
        Command::List(ref list_args) => {
+
            list(
+
                issues,
+
                &list_args.assigned,
+
                &((&list_args.state).into()),
+
                &profile,
+
                args.verbose,
+
            )?;
        }
        Command::Delete { id } => {
            let id = id.resolve(&repo.backend)?;
modified crates/radicle-cli/src/commands/issue/args.rs
@@ -58,6 +58,10 @@ pub struct Args {
    #[arg(long, short)]
    #[clap(global = true)]
    pub(crate) verbose: bool,
+

+
    /// Arguments for the empty subcommand. Will fallback to [`Command::List`].
+
    #[clap(flatten)]
+
    pub(crate) empty: EmptyArgs,
}

/// Commands to create, view, and edit Radicle issues
@@ -205,10 +209,40 @@ impl Command {
    }
}

-
impl Default for Command {
-
    fn default() -> Self {
-
        Self::List(ListArgs::default())
-
    }
+
/// Arguments for the empty subcommand.
+
#[derive(Parser, Debug, Default)]
+
pub(crate) struct EmptyArgs {
+
    #[arg(long, name = "DID")]
+
    #[arg(default_missing_value = "me")]
+
    #[arg(num_args = 0..=1)]
+
    #[arg(hide = true)]
+
    #[allow(clippy::missing_docs_in_private_items)] // flattened
+
    pub(crate) assigned: Option<Assigned>,
+

+
    #[clap(flatten)]
+
    #[allow(clippy::missing_docs_in_private_items)] // flattened
+
    pub(crate) state: EmptyStateArgs,
+
}
+

+
/// Counterpart to [`ListStateArgs`] for the empty subcommand.
+
#[derive(Parser, Debug, Default)]
+
#[group(id = "state", required = false, multiple = false)]
+
pub(crate) struct EmptyStateArgs {
+
    #[arg(long, hide = true)]
+
    #[allow(clippy::missing_docs_in_private_items)] // hidden
+
    all: bool,
+

+
    #[arg(long, hide = true)]
+
    #[allow(clippy::missing_docs_in_private_items)] // hidden
+
    open: bool,
+

+
    #[arg(long, hide = true)]
+
    #[allow(clippy::missing_docs_in_private_items)] // hidden
+
    closed: bool,
+

+
    #[arg(long, hide = true)]
+
    #[allow(clippy::missing_docs_in_private_items)] // hidden
+
    solved: bool,
}

/// Arguments for the [`Command::List`] subcommand.
@@ -220,35 +254,63 @@ pub(crate) struct ListArgs {
    #[arg(num_args = 0..=1)]
    pub(crate) assigned: Option<Assigned>,

+
    #[clap(flatten)]
+
    #[allow(clippy::missing_docs_in_private_items)] // flattened
+
    pub(crate) state: ListStateArgs,
+
}
+

+
#[derive(Parser, Debug, Default)]
+
#[group(id = "state", required = false, multiple = false)]
+
pub(crate) struct ListStateArgs {
    /// List all issues
-
    #[arg(long, group = "state")]
+
    #[arg(long)]
    all: bool,

    /// List only open issues (default)
-
    #[arg(long, group = "state")]
+
    #[arg(long)]
    open: bool,

    /// List only closed issues
-
    #[arg(long, group = "state")]
+
    #[arg(long)]
    closed: bool,

    /// List only solved issues
-
    #[arg(long, group = "state")]
+
    #[arg(long)]
    solved: bool,
}

-
impl From<ListArgs> for Option<State> {
-
    fn from(args: ListArgs) -> Self {
+
impl From<&ListStateArgs> for Option<State> {
+
    fn from(args: &ListStateArgs) -> Self {
        match (args.all, args.open, args.closed, args.solved) {
-
            (true, _, _, _) => None,
-
            (_, true, _, _) => Some(State::Open),
-
            (_, _, true, _) => Some(State::Closed {
+
            (true, false, false, false) => None,
+
            (false, true, false, false) | (false, false, false, false) => Some(State::Open),
+
            (false, false, true, false) => Some(State::Closed {
                reason: CloseReason::Other,
            }),
-
            (_, _, _, true) => Some(State::Closed {
+
            (false, false, false, true) => Some(State::Closed {
                reason: CloseReason::Solved,
            }),
-
            _ => Some(State::Open),
+
            _ => unreachable!(),
+
        }
+
    }
+
}
+

+
impl From<EmptyStateArgs> for ListStateArgs {
+
    fn from(args: EmptyStateArgs) -> Self {
+
        Self {
+
            all: args.all,
+
            open: args.open,
+
            closed: args.closed,
+
            solved: args.solved,
+
        }
+
    }
+
}
+

+
impl From<EmptyArgs> for ListArgs {
+
    fn from(args: EmptyArgs) -> Self {
+
        Self {
+
            assigned: args.assigned,
+
            state: ListStateArgs::from(args.state),
        }
    }
}