1use std::fmt::Display;
2
3use clap::{Parser, Subcommand};
4
5#[derive(Parser, Debug)]
6#[command(author, version = CliArgs::unstable_version(), about, long_about = None)]
7#[command(name = "flake-edit")]
8#[command(next_line_help = true)]
9pub struct CliArgs {
11 #[arg(long)]
14 flake: Option<String>,
15 #[arg(long)]
18 lock_file: Option<String>,
19 #[arg(long, default_value_t = false)]
21 diff: bool,
22 #[arg(long, default_value_t = false)]
24 no_lock: bool,
25 #[arg(long, default_value_t = false)]
27 non_interactive: bool,
28 #[arg(long, default_value_t = false)]
30 no_cache: bool,
31 #[arg(long)]
33 cache: Option<String>,
34 #[arg(long, global = true)]
36 config: Option<String>,
37
38 #[command(subcommand)]
39 subcommand: Command,
40}
41
42#[allow(unused)]
43impl CliArgs {
44 fn unstable_version() -> &'static str {
46 const VERSION: &str = env!("CARGO_PKG_VERSION");
47 let date = option_env!("GIT_DATE").unwrap_or("no_date");
48 let rev = option_env!("GIT_REV").unwrap_or("no_rev");
49 Box::leak(format!("{VERSION} - {date} - {rev}").into_boxed_str())
51 }
52
53 pub fn subcommand(&self) -> &Command {
54 &self.subcommand
55 }
56 pub fn list(&self) -> bool {
57 matches!(self.subcommand, Command::List { .. })
58 }
59 pub fn update(&self) -> bool {
60 matches!(self.subcommand, Command::Update { .. })
61 }
62 pub fn pin(&self) -> bool {
63 matches!(self.subcommand, Command::Pin { .. })
64 }
65 pub fn unpin(&self) -> bool {
66 matches!(self.subcommand, Command::Unpin { .. })
67 }
68 pub fn change(&self) -> bool {
69 matches!(self.subcommand, Command::Change { .. })
70 }
71 pub fn follow(&self) -> bool {
72 matches!(self.subcommand, Command::Follow { .. })
73 }
74
75 pub fn flake(&self) -> Option<&String> {
76 self.flake.as_ref()
77 }
78
79 pub fn lock_file(&self) -> Option<&String> {
80 self.lock_file.as_ref()
81 }
82
83 pub fn diff(&self) -> bool {
84 self.diff
85 }
86
87 pub fn no_lock(&self) -> bool {
88 self.no_lock
89 }
90
91 pub fn non_interactive(&self) -> bool {
92 self.non_interactive
93 }
94
95 pub fn no_cache(&self) -> bool {
96 self.no_cache
97 }
98
99 pub fn cache(&self) -> Option<&String> {
100 self.cache.as_ref()
101 }
102
103 pub fn config(&self) -> Option<&String> {
104 self.config.as_ref()
105 }
106}
107
108#[derive(Subcommand, Debug)]
109pub enum Command {
110 #[clap(alias = "a")]
112 Add {
113 id: Option<String>,
115 uri: Option<String>,
117 #[arg(long)]
118 ref_or_rev: Option<String>,
120 #[arg(long, short)]
122 no_flake: bool,
123 #[arg(long, short)]
125 shallow: bool,
126 },
127 #[clap(alias = "rm")]
129 Remove { id: Option<String> },
130 #[clap(alias = "c")]
132 Change {
133 id: Option<String>,
135 uri: Option<String>,
137 #[arg(long)]
138 ref_or_rev: Option<String>,
140 #[arg(long, short)]
142 shallow: bool,
143 },
144 #[clap(alias = "l")]
146 List {
147 #[arg(long, default_value_t = ListFormat::default())]
148 format: ListFormat,
149 },
150 #[clap(alias = "u")]
152 Update {
153 id: Option<String>,
156 #[arg(long)]
159 init: bool,
160 },
161 #[clap(alias = "p")]
163 Pin {
164 id: Option<String>,
166 rev: Option<String>,
168 },
169 #[clap(alias = "up")]
171 Unpin {
172 id: Option<String>,
174 },
175 #[clap(alias = "f")]
184 Follow {
185 #[arg(long, num_args = 0..=1, default_missing_value = "2")]
190 transitive: Option<usize>,
191 #[arg(trailing_var_arg = true, num_args = 0..)]
193 paths: Vec<std::path::PathBuf>,
194 },
195 #[clap(alias = "af")]
203 AddFollow {
204 input: Option<String>,
207 target: Option<String>,
209 },
210 #[clap(hide = true)]
211 #[command(name = "completion")]
212 Completion {
214 #[arg(long)]
215 inputs: bool,
216 mode: CompletionMode,
217 },
218 #[clap(alias = "cfg", arg_required_else_help = true)]
220 Config {
221 #[arg(long)]
223 print_default: bool,
224 #[arg(long)]
226 path: bool,
227 },
228}
229
230#[derive(Debug, Clone, Default)]
231pub enum CompletionMode {
233 #[default]
234 None,
235 Add,
236 Change,
237 Follow,
238}
239
240impl From<String> for CompletionMode {
241 fn from(value: String) -> Self {
242 use CompletionMode::*;
243 match value.to_lowercase().as_str() {
244 "add" => Add,
245 "change" => Change,
246 "follow" => Follow,
247 _ => None,
248 }
249 }
250}
251
252#[derive(Debug, Clone, Default)]
253pub enum ListFormat {
254 None,
255 Simple,
256 Toplevel,
257 #[default]
258 Detailed,
259 Raw,
260 Json,
261}
262
263impl From<String> for ListFormat {
264 fn from(value: String) -> Self {
265 use ListFormat::*;
266 match value.to_lowercase().as_str() {
267 "detailed" => Detailed,
268 "simple" => Simple,
269 "toplevel" => Toplevel,
270 "raw" => Raw,
271 "json" => Json,
272 _ => None,
273 }
274 }
275}
276
277impl Display for ListFormat {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 match self {
280 ListFormat::None => write!(f, ""),
281 ListFormat::Simple => write!(f, "simple"),
282 ListFormat::Toplevel => write!(f, "toplevel"),
283 ListFormat::Detailed => write!(f, "detailed"),
284 ListFormat::Raw => write!(f, "raw"),
285 ListFormat::Json => write!(f, "json"),
286 }
287 }
288}