1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use clap::{Parser, Subcommand, ValueEnum};
#[derive(Parser, Debug)]
#[command(author, version = CliArgs::unstable_version(), about, long_about)]
#[command(name = "flake-edit")]
#[command(next_line_help = true)]
/// Edit your flake inputs with ease
pub struct CliArgs {
/// Path to `flake.nix`, or a directory containing `flake.nix`.
/// Defaults to `flake.nix` in the current directory.
#[arg(long)]
flake: Option<String>,
/// Location of the `flake.lock` file.
/// Defaults to `flake.lock` in the current directory.
#[arg(long)]
lock_file: Option<String>,
/// Print a diff of the changes instead of writing them to disk.
#[arg(long, default_value_t = false)]
diff: bool,
/// Skip updating the lockfile after editing flake.nix.
#[arg(long, default_value_t = false)]
no_lock: bool,
/// Disable interactive prompts.
#[arg(long, default_value_t = false)]
non_interactive: bool,
/// Disable reading from and writing to the completion cache.
#[arg(long, default_value_t = false)]
no_cache: bool,
/// Path to a custom cache file.
#[arg(long)]
cache: Option<String>,
/// Path to a custom configuration file.
#[arg(long, global = true)]
config: Option<String>,
#[command(subcommand)]
subcommand: Command,
}
impl CliArgs {
/// Version string with embedded git revision and date, when available.
fn unstable_version() -> &'static str {
const VERSION: &str = env!("CARGO_PKG_VERSION");
let date = option_env!("GIT_DATE").unwrap_or("no_date");
let rev = option_env!("GIT_REV").unwrap_or("no_rev");
// Leaks per call. Only invoked once at startup.
Box::leak(format!("{VERSION} - {date} - {rev}").into_boxed_str())
}
pub fn subcommand(&self) -> &Command {
&self.subcommand
}
pub fn flake(&self) -> Option<&String> {
self.flake.as_ref()
}
pub fn lock_file(&self) -> Option<&String> {
self.lock_file.as_ref()
}
pub fn diff(&self) -> bool {
self.diff
}
pub fn no_lock(&self) -> bool {
self.no_lock
}
pub fn non_interactive(&self) -> bool {
self.non_interactive
}
pub fn no_cache(&self) -> bool {
self.no_cache
}
pub fn cache(&self) -> Option<&String> {
self.cache.as_ref()
}
pub fn config(&self) -> Option<&String> {
self.config.as_ref()
}
}
#[derive(Subcommand, Debug)]
pub enum Command {
/// Add a new flake reference.
#[clap(alias = "a")]
Add {
/// The name of an input attribute.
id: Option<String>,
/// The uri that should be added to the input.
uri: Option<String>,
#[arg(long)]
/// Pin to a specific ref_or_rev
ref_or_rev: Option<String>,
/// The input itself is not a flake.
#[arg(long, short)]
no_flake: bool,
/// Use shallow clone for the input.
#[arg(long, short)]
shallow: bool,
},
/// Remove a specific flake reference based on its id.
#[clap(alias = "rm")]
Remove { id: Option<String> },
/// Change an existing flake reference's URI.
#[clap(alias = "c")]
Change {
/// The name of an existing input attribute.
id: Option<String>,
/// The new URI for the input.
uri: Option<String>,
#[arg(long)]
/// Pin to a specific ref_or_rev
ref_or_rev: Option<String>,
/// Use shallow clone for the input.
#[arg(long, short)]
shallow: bool,
},
/// List flake inputs
#[clap(alias = "l")]
List {
#[arg(long, value_enum, default_value_t = ListFormat::default())]
format: ListFormat,
},
/// Update inputs to their latest specified release.
#[clap(alias = "u")]
Update {
/// The id of an input attribute.
/// If omitted will update all inputs.
id: Option<String>,
/// Whether the latest semver release of the remote should be used even thought the release
/// itself isn't yet pinned to a specific release.
#[arg(long)]
init: bool,
},
/// Pin inputs to their current or a specified rev.
#[clap(alias = "p")]
Pin {
/// The id of an input attribute.
id: Option<String>,
/// Optionally specify a rev for the inputs attribute.
rev: Option<String>,
},
/// Unpin an input so it tracks the upstream default again.
#[clap(alias = "up")]
Unpin {
/// The id of an input attribute.
id: Option<String>,
},
/// Automatically add and remove follows declarations.
///
/// Analyzes the flake.lock to find nested inputs that match top-level inputs,
/// then adds appropriate follows declarations and removes stale ones.
///
/// With file paths, processes multiple flakes in batch.
/// For every `flake.nix` file passed in it will assume a
/// `flake.lock` file exists in the same directory.
#[clap(alias = "f")]
Follow {
/// Enable transitive follows deduplication, promoting shared nested
/// inputs to top-level when they appear at least N times.
/// Defaults to 2 if no value is given. Overrides the config file's
/// `follow.transitive_min`.
#[arg(long, num_args = 0..=1, default_missing_value = "2")]
transitive: Option<usize>,
/// Maximum depth of follows declarations to write.
/// Omitting the flag writes follows at every depth the lockfile
/// graph supports. `--depth N` caps emission: 1 writes only
/// `parent.child.follows`, 2 also writes
/// `parent.child.grandchild.follows`, and so on. Overrides the
/// config file's `follow.max_depth`.
#[arg(long)]
depth: Option<usize>,
/// Flake.nix paths to process. If empty, runs on current directory.
#[arg(trailing_var_arg = true, num_args = 0..)]
paths: Vec<std::path::PathBuf>,
},
/// Manually add a single follows declaration.
///
/// Example: `flake-edit add-follow rust-overlay.nixpkgs nixpkgs`
///
/// This creates: `rust-overlay.inputs.nixpkgs.follows = "nixpkgs";`
///
/// Without arguments, starts an interactive selection.
#[clap(alias = "af")]
AddFollow {
/// The input path in dot notation (e.g., "rust-overlay.nixpkgs" means
/// the nixpkgs input of rust-overlay).
input: Option<String>,
/// The target input to follow (e.g., "nixpkgs").
target: Option<String>,
},
#[clap(hide = true)]
#[command(name = "completion")]
/// Meant for shell completions.
Completion {
#[arg(long)]
inputs: bool,
#[arg(value_enum)]
mode: CompletionMode,
},
/// Manage flake-edit configuration.
#[clap(alias = "cfg", arg_required_else_help = true)]
Config {
/// Output the default configuration to stdout.
#[arg(long)]
print_default: bool,
/// Show where configuration would be loaded from.
#[arg(long)]
path: bool,
},
}
/// Which subcommand to complete.
#[derive(Debug, Clone, ValueEnum)]
pub enum CompletionMode {
Add,
Change,
Follow,
}
/// Output format for the `list` subcommand.
#[derive(Debug, Clone, Default, ValueEnum)]
pub enum ListFormat {
Simple,
Toplevel,
#[default]
Detailed,
Json,
}