Skip to main content

radicle_cli/commands/init/
args.rs

1use std::path::PathBuf;
2
3use clap::Parser;
4use radicle::{
5    identity::{Visibility, project::ProjectName},
6    node::policy::Scope,
7    prelude::RepoId,
8};
9use radicle_term::Interactive;
10
11use crate::terminal::args::ScopeParser;
12
13const ABOUT: &str = "Initialize a Radicle repository";
14
15#[derive(Debug, Parser)]
16#[command(about = ABOUT, disable_version_flag = true)]
17pub struct Args {
18    /// Directory to be initialized
19    pub(super) path: Option<PathBuf>,
20    /// Name of the repository
21    #[arg(long)]
22    pub(super) name: Option<ProjectName>,
23    /// Description of the repository
24    #[arg(long)]
25    pub(super) description: Option<String>,
26    /// The default branch of the repository
27    #[arg(long = "default-branch")]
28    pub(super) branch: Option<String>,
29    /// Repository follow scope
30    #[arg(
31        long,
32        default_value_t = Scope::All,
33        value_name = "SCOPE",
34        value_parser = ScopeParser,
35    )]
36    pub(super) scope: Scope,
37    /// Set repository visibility to *private*
38    #[arg(long, conflicts_with = "public")]
39    private: bool,
40    /// Set repository visibility to *public*
41    #[arg(long, conflicts_with = "private")]
42    public: bool,
43    /// Set up repository as an existing Radicle repository
44    ///
45    /// [example values: rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH, z3Tr6bC7ctEg2EHmLvknUr29mEDLH]
46    #[arg(long, value_name = "RID")]
47    pub(super) existing: Option<RepoId>,
48    /// Set up the upstream of the default branch
49    #[arg(short = 'u', long)]
50    pub(super) set_upstream: bool,
51    /// Set up the Radicle key as a signing key for this repository
52    #[arg(long)]
53    pub(super) setup_signing: bool,
54    /// Don't ask for confirmation during setup
55    #[arg(long)]
56    no_confirm: bool,
57    /// Don't seed this repository after initializing it
58    #[arg(long)]
59    no_seed: bool,
60    /// Verbose mode
61    #[arg(short, long)]
62    pub(super) verbose: bool,
63}
64
65impl Args {
66    pub(super) fn interactive(&self) -> Interactive {
67        if self.no_confirm {
68            Interactive::No
69        } else {
70            Interactive::Yes
71        }
72    }
73
74    pub(super) fn visibility(&self) -> Option<Visibility> {
75        if self.private {
76            debug_assert!(!self.public, "BUG: `private` and `public` should conflict");
77            Some(Visibility::private([]))
78        } else if self.public {
79            Some(Visibility::Public)
80        } else {
81            None
82        }
83    }
84
85    pub(super) fn seed(&self) -> bool {
86        !self.no_seed
87    }
88}
89
90#[cfg(test)]
91mod test {
92    use super::Args;
93    use clap::Parser;
94    use clap::error::ErrorKind;
95
96    #[test]
97    fn should_parse_rid_non_urn() {
98        let args = Args::try_parse_from(["init", "--existing", "z3Tr6bC7ctEg2EHmLvknUr29mEDLH"]);
99        assert!(args.is_ok())
100    }
101
102    #[test]
103    fn should_parse_rid_urn() {
104        let args =
105            Args::try_parse_from(["init", "--existing", "rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH"]);
106        assert!(args.is_ok())
107    }
108
109    #[test]
110    fn should_not_parse_rid_url() {
111        let err =
112            Args::try_parse_from(["init", "--existing", "rad://z3Tr6bC7ctEg2EHmLvknUr29mEDLH"])
113                .unwrap_err();
114        assert_eq!(err.kind(), ErrorKind::ValueValidation);
115    }
116}