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)]
13 flake: Option<String>,
14 #[arg(long)]
16 lock_file: Option<String>,
17 #[arg(long, default_value_t = false)]
19 diff: bool,
20 #[arg(long, default_value_t = false)]
22 no_lock: bool,
23 #[arg(long, default_value_t = false)]
25 non_interactive: bool,
26 #[arg(long, default_value_t = false)]
28 no_cache: bool,
29 #[arg(long)]
31 cache: Option<String>,
32
33 #[command(subcommand)]
34 subcommand: Command,
35}
36
37#[allow(unused)]
38impl CliArgs {
39 fn unstable_version() -> &'static str {
41 const VERSION: &str = env!("CARGO_PKG_VERSION");
42 let date = option_env!("GIT_DATE").unwrap_or("no_date");
43 let rev = option_env!("GIT_REV").unwrap_or("no_rev");
44 Box::leak(format!("{VERSION} - {date} - {rev}").into_boxed_str())
46 }
47
48 pub fn subcommand(&self) -> &Command {
49 &self.subcommand
50 }
51 pub fn list(&self) -> bool {
52 matches!(self.subcommand, Command::List { .. })
53 }
54 pub fn update(&self) -> bool {
55 matches!(self.subcommand, Command::Update { .. })
56 }
57 pub fn pin(&self) -> bool {
58 matches!(self.subcommand, Command::Pin { .. })
59 }
60 pub fn unpin(&self) -> bool {
61 matches!(self.subcommand, Command::Unpin { .. })
62 }
63 pub fn change(&self) -> bool {
64 matches!(self.subcommand, Command::Change { .. })
65 }
66 pub fn follow(&self) -> bool {
67 matches!(self.subcommand, Command::Follow { .. })
68 }
69
70 pub fn flake(&self) -> Option<&String> {
71 self.flake.as_ref()
72 }
73
74 pub fn lock_file(&self) -> Option<&String> {
75 self.lock_file.as_ref()
76 }
77
78 pub fn diff(&self) -> bool {
79 self.diff
80 }
81
82 pub fn no_lock(&self) -> bool {
83 self.no_lock
84 }
85
86 pub fn non_interactive(&self) -> bool {
87 self.non_interactive
88 }
89
90 pub fn no_cache(&self) -> bool {
91 self.no_cache
92 }
93
94 pub fn cache(&self) -> Option<&String> {
95 self.cache.as_ref()
96 }
97}
98
99#[derive(Subcommand, Debug)]
100pub enum Command {
101 #[clap(alias = "a")]
103 Add {
104 id: Option<String>,
106 uri: Option<String>,
108 #[arg(long)]
109 ref_or_rev: Option<String>,
111 #[arg(long, short)]
113 no_flake: bool,
114 #[arg(long, short)]
116 shallow: bool,
117 },
118 #[clap(alias = "rm")]
120 Remove { id: Option<String> },
121 #[clap(alias = "c")]
123 Change {
124 id: Option<String>,
126 uri: Option<String>,
128 #[arg(long)]
129 ref_or_rev: Option<String>,
131 #[arg(long, short)]
133 shallow: bool,
134 },
135 #[clap(alias = "l")]
137 List {
138 #[arg(long, default_value_t = ListFormat::default())]
139 format: ListFormat,
140 },
141 #[clap(alias = "u")]
143 Update {
144 id: Option<String>,
147 #[arg(long)]
150 init: bool,
151 },
152 #[clap(alias = "p")]
154 Pin {
155 id: Option<String>,
157 rev: Option<String>,
159 },
160 #[clap(alias = "up")]
162 Unpin {
163 id: Option<String>,
165 },
166 #[clap(alias = "f")]
174 Follow {
175 input: Option<String>,
178 target: Option<String>,
180 #[arg(long, short)]
182 auto: bool,
183 },
184 #[clap(hide = true)]
185 #[command(name = "completion")]
186 Completion {
188 #[arg(long)]
189 inputs: bool,
190 mode: CompletionMode,
191 },
192 #[clap(alias = "cfg", arg_required_else_help = true)]
194 Config {
195 #[arg(long)]
197 print_default: bool,
198 #[arg(long)]
200 path: bool,
201 },
202}
203
204#[derive(Debug, Clone, Default)]
205pub enum CompletionMode {
207 #[default]
208 None,
209 Add,
210 Change,
211 Follow,
212}
213
214impl From<String> for CompletionMode {
215 fn from(value: String) -> Self {
216 use CompletionMode::*;
217 match value.to_lowercase().as_str() {
218 "add" => Add,
219 "change" => Change,
220 "follow" => Follow,
221 _ => None,
222 }
223 }
224}
225
226#[derive(Debug, Clone, Default)]
227pub enum ListFormat {
228 None,
229 Simple,
230 Toplevel,
231 #[default]
232 Detailed,
233 Raw,
234 Json,
235}
236
237impl From<String> for ListFormat {
238 fn from(value: String) -> Self {
239 use ListFormat::*;
240 match value.to_lowercase().as_str() {
241 "detailed" => Detailed,
242 "simple" => Simple,
243 "toplevel" => Toplevel,
244 "raw" => Raw,
245 "json" => Json,
246 _ => None,
247 }
248 }
249}
250
251impl Display for ListFormat {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 match self {
254 ListFormat::None => write!(f, ""),
255 ListFormat::Simple => write!(f, "simple"),
256 ListFormat::Toplevel => write!(f, "toplevel"),
257 ListFormat::Detailed => write!(f, "detailed"),
258 ListFormat::Raw => write!(f, "raw"),
259 ListFormat::Json => write!(f, "json"),
260 }
261 }
262}