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)]
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(trailing_var_arg = true, num_args = 0..)]
187 paths: Vec<std::path::PathBuf>,
188 },
189 #[clap(alias = "af")]
197 AddFollow {
198 input: Option<String>,
201 target: Option<String>,
203 },
204 #[clap(hide = true)]
205 #[command(name = "completion")]
206 Completion {
208 #[arg(long)]
209 inputs: bool,
210 mode: CompletionMode,
211 },
212 #[clap(alias = "cfg", arg_required_else_help = true)]
214 Config {
215 #[arg(long)]
217 print_default: bool,
218 #[arg(long)]
220 path: bool,
221 },
222}
223
224#[derive(Debug, Clone, Default)]
225pub enum CompletionMode {
227 #[default]
228 None,
229 Add,
230 Change,
231 Follow,
232}
233
234impl From<String> for CompletionMode {
235 fn from(value: String) -> Self {
236 use CompletionMode::*;
237 match value.to_lowercase().as_str() {
238 "add" => Add,
239 "change" => Change,
240 "follow" => Follow,
241 _ => None,
242 }
243 }
244}
245
246#[derive(Debug, Clone, Default)]
247pub enum ListFormat {
248 None,
249 Simple,
250 Toplevel,
251 #[default]
252 Detailed,
253 Raw,
254 Json,
255}
256
257impl From<String> for ListFormat {
258 fn from(value: String) -> Self {
259 use ListFormat::*;
260 match value.to_lowercase().as_str() {
261 "detailed" => Detailed,
262 "simple" => Simple,
263 "toplevel" => Toplevel,
264 "raw" => Raw,
265 "json" => Json,
266 _ => None,
267 }
268 }
269}
270
271impl Display for ListFormat {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match self {
274 ListFormat::None => write!(f, ""),
275 ListFormat::Simple => write!(f, "simple"),
276 ListFormat::Toplevel => write!(f, "toplevel"),
277 ListFormat::Detailed => write!(f, "detailed"),
278 ListFormat::Raw => write!(f, "raw"),
279 ListFormat::Json => write!(f, "json"),
280 }
281 }
282}