Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
cli/follow: Use clap
Merged did:key:z6MkgFq6...nBGz opened 6 months ago
6 files changed +63 -113 06e22434 407abc6a
modified crates/radicle-cli/src/commands/follow.rs
@@ -1,106 +1,21 @@
-
use std::ffi::OsString;
-

-
use anyhow::anyhow;
+
mod args;

use radicle::node::{policy, Alias, AliasStore, Handle, NodeId};
use radicle::{prelude::*, Node};
use radicle_term::{Element as _, Paint, Table};

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

-
pub const HELP: Help = Help {
-
    name: "follow",
-
    description: "Manage node follow policies",
-
    version: env!("RADICLE_VERSION"),
-
    usage: r#"
-
Usage
-

-
    rad follow [<nid>] [--alias <name>] [<option>...]
-

-
    The `follow` command will print all nodes being followed, optionally filtered by alias, if no
-
    Node ID is provided.
-
    Otherwise, it takes a Node ID, optionally in DID format, and updates the follow policy
-
    for that peer, optionally giving the peer the alias provided.
-

-
Options
-

-
    --alias <name>         Associate an alias to a followed peer
-
    --verbose, -v          Verbose output
-
    --help                 Print help
-
"#,
-
};
-

-
#[derive(Debug)]
-
pub enum Operation {
-
    Follow { nid: NodeId, alias: Option<Alias> },
-
    List { alias: Option<Alias> },
-
}
-

-
#[derive(Debug, Default)]
-
pub enum OperationName {
-
    Follow,
-
    #[default]
-
    List,
-
}
-

-
#[derive(Debug)]
-
pub struct Options {
-
    pub op: Operation,
-
    pub verbose: bool,
-
}

-
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 verbose = false;
-
        let mut nid: Option<NodeId> = None;
-
        let mut alias: Option<Alias> = None;
-

-
        while let Some(arg) = parser.next()? {
-
            match &arg {
-
                Value(val) if nid.is_none() => {
-
                    if let Ok(did) = term::args::did(val) {
-
                        nid = Some(did.into());
-
                    } else if let Ok(val) = term::args::nid(val) {
-
                        nid = Some(val);
-
                    } else {
-
                        anyhow::bail!("invalid Node ID `{}` specified", val.to_string_lossy());
-
                    }
-
                }
-
                Long("alias") if alias.is_none() => {
-
                    let name = parser.value()?;
-
                    let name = term::args::alias(&name)?;
-

-
                    alias = Some(name.to_owned());
-
                }
-
                Long("verbose") | Short('v') => verbose = true,
-
                Long("help") | Short('h') => {
-
                    return Err(Error::Help.into());
-
                }
-
                _ => {
-
                    return Err(anyhow!(arg.unexpected()));
-
                }
-
            }
-
        }
-

-
        let op = match nid {
-
            Some(nid) => Operation::Follow { nid, alias },
-
            None => Operation::List { alias },
-
        };
-
        Ok((Options { op, verbose }, vec![]))
-
    }
-
}
+
pub use args::Args;
+
pub(crate) use args::ABOUT;

-
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
+
pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
    let profile = ctx.profile()?;
    let mut node = radicle::Node::new(profile.socket());

-
    match options.op {
-
        Operation::Follow { nid, alias } => follow(nid, alias, &mut node, &profile)?,
-
        Operation::List { alias } => following(&profile, alias)?,
+
    match args.nid {
+
        Some(nid) => follow(nid, args.alias, &mut node, &profile)?,
+
        None => following(&profile, args.alias)?,
    }

    Ok(())
added crates/radicle-cli/src/commands/follow/args.rs
@@ -0,0 +1,30 @@
+
use clap::Parser;
+

+
use radicle::node::{Alias, NodeId};
+

+
use crate::terminal as term;
+

+
pub(crate) const ABOUT: &str = "Manage node follow policies";
+

+
const LONG_ABOUT: &str = r#"
+
The `follow` command will print all nodes being followed, optionally filtered by alias, if no
+
Node ID is provided.
+
Otherwise, it takes a Node ID, optionally in DID format, and updates the follow policy
+
for that peer, optionally giving the peer the alias provided.
+
"#;
+

+
#[derive(Parser, Debug)]
+
#[command(about = ABOUT, long_about = LONG_ABOUT, disable_version_flag = true)]
+
pub struct Args {
+
    /// The DID or Node ID of the peer to follow
+
    #[arg(value_parser = term::args::parse_nid)]
+
    pub(crate) nid: Option<NodeId>,
+

+
    /// Associate an alias to a followed peer
+
    #[arg(long)]
+
    pub(crate) alias: Option<Alias>,
+

+
    /// Verbose output
+
    #[arg(long, short)]
+
    pub verbose: bool,
+
}
modified crates/radicle-cli/src/commands/help.rs
@@ -97,7 +97,10 @@ const COMMANDS: &[CommandItem] = &[
        name: "seed",
        about: crate::commands::seed::ABOUT,
    },
-
    CommandItem::Lexopt(crate::commands::follow::HELP),
+
    CommandItem::Clap {
+
        name: "follow",
+
        about: crate::commands::follow::ABOUT,
+
    },
    CommandItem::Clap {
        name: "unblock",
        about: crate::commands::unblock::ABOUT,
modified crates/radicle-cli/src/commands/unfollow/args.rs
@@ -1,9 +1,8 @@
use clap::Parser;

-
use thiserror::Error;
-

use radicle::node::NodeId;
-
use radicle::prelude::Did;
+

+
use crate::terminal as term;

pub(crate) const ABOUT: &str = "Unfollow a peer";

@@ -11,26 +10,11 @@ const LONG_ABOUT: &str = r#"
The `unfollow` command takes a Node ID, optionally in DID format,
and removes the follow policy for that peer."#;

-
#[derive(Debug, Error)]
-
#[error("invalid Node ID specified (Node ID parsing failed with: '{nid}', DID parsing failed with: '{did}'))")]
-
struct NodeIdParseError {
-
    did: radicle::identity::did::DidError,
-
    nid: radicle::crypto::PublicKeyError,
-
}
-

-
fn parse_nid(value: &str) -> Result<NodeId, NodeIdParseError> {
-
    value.parse::<Did>().map(NodeId::from).or_else(|did| {
-
        value
-
            .parse::<NodeId>()
-
            .map_err(|nid| NodeIdParseError { nid, did })
-
    })
-
}
-

#[derive(Debug, Parser)]
#[command(about = ABOUT, long_about = LONG_ABOUT, disable_version_flag = true)]
pub struct Args {
    /// Node ID (optionally in DID format) of the peer to unfollow
-
    #[arg(value_name = "NID", value_parser = parse_nid)]
+
    #[arg(value_name = "NID", value_parser = term::args::parse_nid)]
    pub(super) nid: NodeId,

    /// Verbose output
modified crates/radicle-cli/src/main.rs
@@ -62,6 +62,7 @@ enum Commands {
    #[command(external_subcommand, hide = true)]
    Diff(Vec<OsString>),

+
    Follow(follow::Args),
    Fork(fork::Args),
    Id(id::Args),
    Init(init::Args),
@@ -239,7 +240,9 @@ pub(crate) fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyho
            }
        }
        "follow" => {
-
            term::run_command_args::<follow::Options, _>(follow::HELP, follow::run, args.to_vec());
+
            if let Some(Commands::Follow(args)) = CliArgs::parse().command {
+
                term::run_command_fn(follow::run, args);
+
            }
        }
        "fork" => {
            if let Some(Commands::Fork(args)) = CliArgs::parse().command {
modified crates/radicle-cli/src/terminal/args.rs
@@ -244,6 +244,21 @@ impl std::fmt::Display for BlockTarget {
    }
}

+
#[derive(Debug, thiserror::Error)]
+
#[error("invalid Node ID specified (Node ID parsing failed with: '{nid}', DID parsing failed with: '{did}'))")]
+
pub(crate) struct NodeIdParseError {
+
    did: radicle::identity::did::DidError,
+
    nid: radicle::crypto::PublicKeyError,
+
}
+

+
pub(crate) fn parse_nid(value: &str) -> Result<NodeId, NodeIdParseError> {
+
    value.parse::<Did>().map(NodeId::from).or_else(|did| {
+
        value
+
            .parse::<NodeId>()
+
            .map_err(|nid| NodeIdParseError { nid, did })
+
    })
+
}
+

#[derive(Clone, Debug)]
pub(crate) struct ScopeParser;