Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
cli/inspect: use Clap
Merged levitte opened 6 months ago
4 files changed +136 -118 5ed1b8e5 990e22ac
modified crates/radicle-cli/src/commands/help.rs
@@ -72,7 +72,10 @@ const COMMANDS: &[CommandItem] = &[
        about: crate::commands::init::ABOUT,
    },
    CommandItem::Lexopt(crate::commands::inbox::HELP),
-
    CommandItem::Lexopt(crate::commands::inspect::HELP),
+
    CommandItem::Clap {
+
        name: "inspect",
+
        about: crate::commands::inspect::ABOUT,
+
    },
    CommandItem::Clap {
        name: "issue",
        about: crate::commands::issue::ABOUT,
modified crates/radicle-cli/src/commands/inspect.rs
@@ -1,6 +1,8 @@
#![allow(clippy::or_fun_call)]
+

+
mod args;
+

use std::collections::HashMap;
-
use std::ffi::OsString;
use std::path::Path;
use std::str::FromStr;

@@ -16,134 +18,40 @@ use radicle::storage::refs::RefsAt;
use radicle::storage::{ReadRepository, ReadStorage};

use crate::terminal as term;
-
use crate::terminal::args::{Args, Error, Help};
use crate::terminal::json;
use crate::terminal::Element;

-
pub const HELP: Help = Help {
-
    name: "inspect",
-
    description: "Inspect a Radicle repository",
-
    version: env!("RADICLE_VERSION"),
-
    usage: r#"
-
Usage
-

-
    rad inspect <path> [<option>...]
-
    rad inspect <rid>  [<option>...]
-
    rad inspect [<option>...]
-

-
    Inspects the given path or RID. If neither is specified,
-
    the current repository is inspected.
-

-
Options
-

-
    --rid        Return the repository identifier (RID)
-
    --payload    Inspect the repository's identity payload
-
    --refs       Inspect the repository's refs on the local device
-
    --sigrefs    Inspect the values of `rad/sigrefs` for all remotes of this repository
-
    --identity   Inspect the identity document
-
    --visibility Inspect the repository's visibility
-
    --delegates  Inspect the repository's delegates
-
    --policy     Inspect the repository's seeding policy
-
    --history    Show the history of the repository identity document
-
    --help       Print help
-
"#,
-
};
-

-
#[derive(Default, Debug, Eq, PartialEq)]
-
pub enum Target {
-
    Refs,
-
    Payload,
-
    Delegates,
-
    Identity,
-
    Visibility,
-
    Sigrefs,
-
    Policy,
-
    History,
-
    #[default]
-
    RepoId,
-
}
-

-
#[derive(Default, Debug, Eq, PartialEq)]
-
pub struct Options {
-
    pub rid: Option<RepoId>,
-
    pub target: Target,
-
}
-

-
impl Args for Options {
-
    fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
-
        use lexopt::prelude::*;
-

-
        let mut parser = lexopt::Parser::from_args(args);
-
        let mut rid: Option<RepoId> = None;
-
        let mut target = Target::default();
+
pub use args::Args;
+
use args::Target;
+
pub(crate) use args::ABOUT;

-
        while let Some(arg) = parser.next()? {
-
            match arg {
-
                Long("help") | Short('h') => {
-
                    return Err(Error::Help.into());
-
                }
-
                Long("refs") => {
-
                    target = Target::Refs;
-
                }
-
                Long("payload") => {
-
                    target = Target::Payload;
-
                }
-
                Long("policy") => {
-
                    target = Target::Policy;
-
                }
-
                Long("delegates") => {
-
                    target = Target::Delegates;
-
                }
-
                Long("history") => {
-
                    target = Target::History;
-
                }
-
                Long("identity") => {
-
                    target = Target::Identity;
-
                }
-
                Long("sigrefs") => {
-
                    target = Target::Sigrefs;
-
                }
-
                Long("rid") => {
-
                    target = Target::RepoId;
-
                }
-
                Long("visibility") => {
-
                    target = Target::Visibility;
-
                }
-
                Value(val) if rid.is_none() => {
-
                    let val = val.to_string_lossy();
-

-
                    if let Ok(val) = RepoId::from_str(&val) {
-
                        rid = Some(val);
-
                    } else {
-
                        rid = radicle::rad::at(Path::new(val.as_ref()))
-
                            .map(|(_, id)| Some(id))
-
                            .context("Supplied argument is not a valid path")?;
-
                    }
-
                }
-
                _ => anyhow::bail!(arg.unexpected()),
+
pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
+
    let rid = match args.repo {
+
        Some(rid) => {
+
            if let Ok(val) = RepoId::from_str(&rid) {
+
                val
+
            } else {
+
                radicle::rad::at(Path::new(&rid))
+
                    .map(|(_, id)| id)
+
                    .context("Supplied argument is not a valid path")?
            }
        }
-

-
        Ok((Options { rid, target }, vec![]))
-
    }
-
}
-

-
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
-
    let rid = match options.rid {
-
        Some(rid) => rid,
        None => radicle::rad::cwd()
            .map(|(_, rid)| rid)
            .context("Current directory is not a Radicle repository")?,
    };

-
    if options.target == Target::RepoId {
+
    let target = args.target.into();
+

+
    if matches!(target, Target::RepoId) {
        term::info!("{}", term::format::highlight(rid.urn()));
        return Ok(());
    }
+

    let profile = ctx.profile()?;
    let storage = &profile.storage;

-
    match options.target {
+
    match target {
        Target::Refs => {
            let (repo, _) = repo(rid, storage)?;
            refs(&repo)?;
added crates/radicle-cli/src/commands/inspect/args.rs
@@ -0,0 +1,97 @@
+
use clap::Parser;
+

+
pub(crate) const ABOUT: &str = "Inspect a Radicle repository";
+
const LONG_ABOUT: &str = r#"Inspects the given path or RID. If neither is specified,
+
the current repository is inspected.
+
"#;
+

+
#[derive(Debug, Parser)]
+
#[group(multiple = false)]
+
pub(super) struct TargetArgs {
+
    /// Inspect the repository's delegates
+
    #[arg(long)]
+
    pub(super) delegates: bool,
+

+
    /// Show the history of the repository identity document
+
    #[arg(long)]
+
    pub(super) history: bool,
+

+
    /// Inspect the identity document
+
    #[arg(long)]
+
    pub(super) identity: bool,
+

+
    /// Inspect the repository's identity payload
+
    #[arg(long)]
+
    pub(super) payload: bool,
+

+
    /// Inspect the repository's seeding policy
+
    #[arg(long)]
+
    pub(super) policy: bool,
+

+
    /// Inspect the repository's refs on the local device
+
    #[arg(long)]
+
    pub(super) refs: bool,
+

+
    /// Return the repository identifier (RID)
+
    #[arg(long)]
+
    pub(super) rid: bool,
+

+
    /// Inspect the values of `rad/sigrefs` for all remotes of this repository
+
    #[arg(long)]
+
    pub(super) sigrefs: bool,
+

+
    /// Inspect the repository's visibility
+
    #[arg(long)]
+
    pub(super) visibility: bool,
+
}
+

+
pub(super) enum Target {
+
    Delegates,
+
    History,
+
    Identity,
+
    Payload,
+
    Policy,
+
    Refs,
+
    RepoId,
+
    Sigrefs,
+
    Visibility,
+
}
+

+
impl From<TargetArgs> for Target {
+
    fn from(args: TargetArgs) -> Self {
+
        match (
+
            args.delegates,
+
            args.history,
+
            args.identity,
+
            args.payload,
+
            args.policy,
+
            args.refs,
+
            args.rid,
+
            args.sigrefs,
+
            args.visibility,
+
        ) {
+
            (true, false, false, false, false, false, false, false, false) => Target::Delegates,
+
            (false, true, false, false, false, false, false, false, false) => Target::History,
+
            (false, false, true, false, false, false, false, false, false) => Target::Identity,
+
            (false, false, false, true, false, false, false, false, false) => Target::Payload,
+
            (false, false, false, false, true, false, false, false, false) => Target::Policy,
+
            (false, false, false, false, false, true, false, false, false) => Target::Refs,
+
            (false, false, false, false, false, false, true, false, false)
+
            | (false, false, false, false, false, false, false, false, false) => Target::RepoId,
+
            (false, false, false, false, false, false, false, true, false) => Target::Sigrefs,
+
            (false, false, false, false, false, false, false, false, true) => Target::Visibility,
+
            _ => unreachable!(),
+
        }
+
    }
+
}
+

+
#[derive(Debug, Parser)]
+
#[command(about = ABOUT, long_about = LONG_ABOUT, disable_version_flag = true)]
+
pub struct Args {
+
    /// Repository, by RID or by path
+
    #[arg(value_name = "RID|PATH")]
+
    pub(super) repo: Option<String>,
+

+
    #[clap(flatten)]
+
    pub(super) target: TargetArgs,
+
}
modified crates/radicle-cli/src/main.rs
@@ -67,6 +67,7 @@ enum Commands {
    Fork(fork::Args),
    Id(id::Args),
    Init(init::Args),
+
    Inspect(inspect::Args),
    Issue(issue::Args),
    Ls(ls::Args),
    Path(path::Args),
@@ -267,11 +268,20 @@ pub(crate) fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyho
            }
        }
        "inspect" => {
-
            term::run_command_args::<inspect::Options, _>(
-
                inspect::HELP,
-
                inspect::run,
-
                args.to_vec(),
-
            );
+
            let reconstructed_args = {
+
                // This is a horrible workaround to reconstruct the original
+
                // args after having them mangled by our `lexopt`-style parser
+
                // in `parse_args()` in case they were `rad .`.
+
                // TODO: Remove this, when `rad` is fully migrated to `clap`.
+
                vec!["rad", "inspect"]
+
                    .into_iter()
+
                    .map(OsString::from)
+
                    .chain(args.iter().cloned())
+
            };
+

+
            if let Some(Commands::Inspect(args)) = CliArgs::parse_from(reconstructed_args).command {
+
                term::run_command_fn(inspect::run, args);
+
            }
        }
        "issue" => {
            if let Some(Commands::Issue(args)) = CliArgs::parse().command {