radicle_cli/commands/clone/
args.rs1use std::path::PathBuf;
2
3use clap::Parser;
4
5use radicle::identity::doc::RepoId;
6use radicle::identity::IdError;
7use radicle::node::policy::Scope;
8use radicle::prelude::*;
9use radicle::storage::refs;
10
11use crate::common_args::{
12 SignedReferencesFeatureLevel, SignedReferencesFeatureLevelParser,
13 ABOUT_FETCH_SIGNED_REFERENCES_FEATURE_LEVEL_MINIMUM,
14};
15use crate::node::SyncSettings;
16use crate::terminal;
17
18const ABOUT: &str = "Clone a Radicle repository";
19
20const LONG_ABOUT: &str = r#"
21The `clone` command will use your local node's routing table to find seeds from
22which it can clone the repository.
23
24For private repositories, use the `--seed` options, to clone directly
25from known seeds in the privacy set."#;
26
27fn parse_rid(value: &str) -> Result<RepoId, IdError> {
29 value.strip_prefix("rad://").unwrap_or(value).parse()
30}
31
32#[derive(Debug, Parser)]
33pub(super) struct SyncArgs {
34 #[arg(short, long = "seed", value_name = "NID", action = clap::ArgAction::Append)]
36 seeds: Vec<NodeId>,
37
38 #[arg(long, value_parser = humantime::parse_duration, default_value = "9s")]
42 timeout: std::time::Duration,
43
44 #[arg(
45 long,
46 value_parser = SignedReferencesFeatureLevelParser,
47 help = ABOUT_FETCH_SIGNED_REFERENCES_FEATURE_LEVEL_MINIMUM
48 )]
49 signed_refs_feature_level: Option<SignedReferencesFeatureLevel>,
50}
51
52impl From<SyncArgs> for SyncSettings {
53 fn from(args: SyncArgs) -> Self {
54 SyncSettings {
55 timeout: args.timeout,
56 seeds: args.seeds.into_iter().collect(),
57 signed_references_minimum_feature_level: args
58 .signed_refs_feature_level
59 .map(refs::FeatureLevel::from),
60 ..SyncSettings::default()
61 }
62 }
63}
64
65#[derive(Debug, Parser)]
66#[clap(about = ABOUT, long_about = LONG_ABOUT, disable_version_flag = true)]
67pub struct Args {
68 #[arg(value_name = "RID", value_parser = parse_rid)]
72 pub(super) repo: RepoId,
73
74 #[arg(value_name = "PATH")]
76 pub(super) directory: Option<PathBuf>,
77
78 #[arg(
80 long,
81 value_parser = terminal::args::ScopeParser
82 )]
83 pub(super) scope: Option<Scope>,
84
85 #[clap(flatten)]
86 pub(super) sync: SyncArgs,
87
88 #[arg(long)]
90 pub(super) bare: bool,
91
92 #[arg(long, hide = true)]
95 pub(super) no_confirm: bool,
96}
97
98#[cfg(test)]
99mod test {
100 use super::Args;
101 use clap::Parser;
102
103 #[test]
104 fn should_parse_rid_non_urn() {
105 let args = Args::try_parse_from(["clone", "z3Tr6bC7ctEg2EHmLvknUr29mEDLH"]);
106 assert!(args.is_ok())
107 }
108
109 #[test]
110 fn should_parse_rid_urn() {
111 let args = Args::try_parse_from(["clone", "rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH"]);
112 assert!(args.is_ok())
113 }
114
115 #[test]
116 fn should_parse_rid_url() {
117 let args = Args::try_parse_from(["clone", "rad://z3Tr6bC7ctEg2EHmLvknUr29mEDLH"]);
118 assert!(args.is_ok())
119 }
120}