radicle_cli/commands/
clean.rs

1use std::ffi::OsString;
2
3use anyhow::anyhow;
4
5use radicle::identity::RepoId;
6use radicle::storage;
7use radicle::storage::WriteStorage;
8
9use crate::terminal as term;
10use crate::terminal::args::{Args, Error, Help};
11
12pub const HELP: Help = Help {
13    name: "clean",
14    description: "Remove all remotes from a repository",
15    version: env!("RADICLE_VERSION"),
16    usage: r#"
17Usage
18
19    rad clean <rid> [<option>...]
20
21    Removes all remotes from a repository, as long as they are not the
22    local operator or a delegate of the repository.
23
24    Note that remotes will still be fetched as long as they are
25    followed and/or the follow scope is "all".
26
27Options
28
29    --no-confirm        Do not ask for confirmation before removal (default: false)
30    --help              Print help
31"#,
32};
33
34pub struct Options {
35    rid: RepoId,
36    confirm: bool,
37}
38
39impl Args for Options {
40    fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
41        use lexopt::prelude::*;
42
43        let mut parser = lexopt::Parser::from_args(args);
44        let mut id: Option<RepoId> = None;
45        let mut confirm = true;
46
47        while let Some(arg) = parser.next()? {
48            match arg {
49                Long("no-confirm") => {
50                    confirm = false;
51                }
52                Long("help") | Short('h') => {
53                    return Err(Error::Help.into());
54                }
55                Value(val) if id.is_none() => {
56                    id = Some(term::args::rid(&val)?);
57                }
58                _ => anyhow::bail!(arg.unexpected()),
59            }
60        }
61
62        Ok((
63            Options {
64                rid: id
65                    .ok_or_else(|| anyhow!("an RID must be provided; see `rad clean --help`"))?,
66                confirm,
67            },
68            vec![],
69        ))
70    }
71}
72
73pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
74    let profile = ctx.profile()?;
75    let storage = &profile.storage;
76    let rid = options.rid;
77    let path = storage::git::paths::repository(storage, &rid);
78
79    if !path.exists() {
80        anyhow::bail!("repository {rid} was not found");
81    }
82
83    if !options.confirm || term::confirm(format!("Clean {rid}?")) {
84        let cleaned = storage.clean(rid)?;
85        for remote in cleaned {
86            term::info!("Removed {remote}");
87        }
88        term::success!("Successfully cleaned {rid}");
89    }
90
91    Ok(())
92}