radicle_cli/commands/
unfollow.rs

1use std::ffi::OsString;
2
3use anyhow::anyhow;
4
5use radicle::node::{Handle, NodeId};
6
7use crate::terminal as term;
8use crate::terminal::args::{Args, Error, Help};
9
10pub const HELP: Help = Help {
11    name: "unfollow",
12    description: "Unfollow a peer",
13    version: env!("RADICLE_VERSION"),
14    usage: r#"
15Usage
16
17    rad unfollow <nid> [<option>...]
18
19    The `unfollow` command takes a Node ID (<nid>), optionally in DID format,
20    and removes the follow policy for that peer.
21
22Options
23
24    --verbose, -v          Verbose output
25    --help                 Print help
26"#,
27};
28
29#[derive(Debug)]
30pub struct Options {
31    pub nid: NodeId,
32    pub verbose: bool,
33}
34
35impl Args for Options {
36    fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
37        use lexopt::prelude::*;
38
39        let mut parser = lexopt::Parser::from_args(args);
40        let mut nid: Option<NodeId> = None;
41        let mut verbose = false;
42
43        while let Some(arg) = parser.next()? {
44            match &arg {
45                Value(val) if nid.is_none() => {
46                    if let Ok(did) = term::args::did(val) {
47                        nid = Some(did.into());
48                    } else if let Ok(val) = term::args::nid(val) {
49                        nid = Some(val);
50                    } else {
51                        anyhow::bail!("invalid Node ID `{}` specified", val.to_string_lossy());
52                    }
53                }
54                Long("verbose") | Short('v') => verbose = true,
55                Long("help") | Short('h') => {
56                    return Err(Error::Help.into());
57                }
58                _ => {
59                    return Err(anyhow!(arg.unexpected()));
60                }
61            }
62        }
63
64        Ok((
65            Options {
66                nid: nid.ok_or_else(|| anyhow!("a Node ID must be specified"))?,
67                verbose,
68            },
69            vec![],
70        ))
71    }
72}
73
74pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
75    let profile = ctx.profile()?;
76    let mut node = radicle::Node::new(profile.socket());
77    let nid = options.nid;
78
79    let unfollowed = match node.unfollow(nid) {
80        Ok(updated) => updated,
81        Err(e) if e.is_connection_err() => {
82            let mut config = profile.policies_mut()?;
83            config.unfollow(&nid)?
84        }
85        Err(e) => return Err(e.into()),
86    };
87    if unfollowed {
88        term::success!("Follow policy for {} removed", term::format::tertiary(nid),);
89    }
90    Ok(())
91}