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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
//! runix is a library that allows you to **run** **nix** using a typed
//! interface.
//!
//! runix converts command structures into an invocation of a [NixBackend]
//! implementation.
//! The backend currently in development is the [command_line::NixCommandLine]
//! backend, which uses [tokio::process::Command] to `exec` the nix CLI.
//!
//! While this is the reference implmentation, other backends such as an
//! FFI based implementation or Mocking shims for testing are possible.
//!
//! > **Warning**
//! > runix is still in active development!
//! >
//! > It's API is not yet deeply set in stone, more fields will be added as we
//! > expand the coverage of the Nix CLI and traits _may_ change if necessary.
//! >
//! > We greatly appreciate feedback and contributions.
//!
//! # Examples
//!
//! The easiest way to get familiar with the interface is by way of an example.
//!
//! Again, mind that you'll need nix on your `PATH` to use runix.
//!
//! ```no_run
//! # use runix::arguments::source::SourceArgs;
//! # use runix::arguments::NixArgs;
//! # use runix::command::Eval;
//! # use runix::command_line::NixCommandLine;
//! # use runix::Run;
//! # #[tokio::main]
//! # async fn main() {
//! // (1) initialize a backend
//! let cli = NixCommandLine::default();
//!
//! // (2) define the command
//! Eval {
//! source: SourceArgs {
//! expr: Some(r#""Hello Rust""#.into()),
//! },
//! ..Default::default()
//! }
//! // (3) run the command
//! .run(&cli, &NixArgs::default())
//! .await
//! # .unwrap();
//! # }
//! ```
//!
//! This is the runix equivalent to:
//!
//! ```shell
//! $ nix eval --expr '"Hello Rust"'
//! ```
//!
//! While certainly more wordy, than its shell counterpart, its comparable to
//! the same invocation, written manually:
//!
//! ```
//! # async fn with_tokio() {
//! tokio::process::Command::new("nix")
//! .args([
//! "eval".to_string(),
//! "--expr".into(),
//! r#""Hello Rust""#.into(),
//! ])
//! .status()
//! .await
//! # .unwrap();
//! # }
//! ```
//!
//! The main benefit of runix however is that it abstracts away the plain list
//! of arguments and guides you to correct invocations of the cli.
//!
//! # Design goals: Correct, Flexible, Extensible
//!
//! runix' core-interface are the [Run] traits;
//! [Run], [RunJson] and [RunTyped].
//! These traits are implmented for a command and are parametrized by a
//! [NixBackend].
//!
//! runix ships with a backend implementation for the Nix CLI and associated
//! [Run] implementations.
//! The [command_line::NixCliCommand] trait describes an abstract CLI
//! invocation.
//! An implementation of the trait defines how a command type is converted to
//! list of arguments.
//! Many Nix commands share different subsets of arguments - implemented as
//! mixins in Nix' C++ code base.
//! In runix these option groups are represented by individual types.
//!
//! - common arguments valid for all nix commands
//! ([arguments::common::NixCommonArgs])
//! - Nix onfiguration options and nix.conf values
//! ([arguments::config::NixConfigArgs])
//! - arguments for commands that evaluate expressions
//! ([arguments::source::SourceArgs])
//! - flake related options ([arguments::flake::FlakeArgs])
//! - installables:
//! ([arguments::InstallableArg] & [arguments::InstallablesArgs])
//!
//! An implementation of a [command_line::NixCliCommand] then defines
//! which of these groups are applicable,
//! and how they are extracted from an instance of the type.
//! Additionally, some commands have their own specific options that do not
//! fall into one of the larger groups.
//!
//! Splitting groups and in the same way as nix does internally helps runix
//! to generate _correct_ invocations.
//!
//! An exemplary [command_line::NixCliCommand] looks as follows
//!
//! ```
//! # use runix::arguments::InstallablesArgs;
//! # use runix::arguments::eval::EvaluationArgs;
//! # use runix::arguments::flake::FlakeArgs;
//! # use runix::arguments::source::SourceArgs;
//! # use runix::command_line::NixCliCommand;
//! # use runix::command_line::Group;
//!
//! // The command interface
//! #[derive(Debug, Default, Clone)]
//! pub struct Shell {
//! pub flake: FlakeArgs,
//! pub eval: EvaluationArgs,
//! pub source: SourceArgs,
//! pub installables: InstallablesArgs,
//! }
//!
//! impl NixCliCommand for Shell {
//! type Own = (); // no specific arguments for Shell
//!
//! // the nix subcommand (`nix shell`)
//! const SUBCOMMAND: &'static [&'static str] = &["shell"];
//!
//! // shell supports three groups of options and multiple installables
//! const EVAL_ARGS: Group<Self, EvaluationArgs> = Some(|d| d.eval.clone());
//! const FLAKE_ARGS: Group<Self, FlakeArgs> = Some(|d| d.flake.clone());
//! const INSTALLABLES: Group<Self, InstallablesArgs> = Some(|d| d.installables.clone());
//! const SOURCE_ARGS: Group<Self, SourceArgs> = Some(|d| d.source.clone());
//! }
//! ```
//!
//! Note that not all groups need to be specified.
//! The trait implements defaults if they are not applicable.
//!
//! All option groups implement the [command_line::ToArgs] trait,
//! to convert the groups to a list of CLI arguments.
//! The fields on an option group typically implement
//! [command_line::flag::Flag].
//! This trait allows declarative definitions of individual flags,
//! and their value formating.
//! A [command_line::flag::Flag] is comprised of an option
//! ([command_line::flag::Flag::FLAG]) and a conversion of the type's content
//! to a list of arguments (driven by [command_line::flag::FlagType]).
//! [command_line::flag::FlagType] implementes the conversion different kinds
//! of arguments, including flags, lists, paths, numbers or manual layouts.
//! ```no_run
//! # use runix::command_line::flag::{Flag, FlagType};
//! # use derive_more::{Deref, From};
//!
//! /// Flag for accept-flake-config
//! #[derive(Clone, From, Debug, Deref, Default)]
//! pub struct ConnectTimeout(u32);
//! impl Flag for ConnectTimeout {
//! const FLAG: &'static str = "--connect-timeout";
//! const FLAG_TYPE: FlagType<Self> = FlagType::number_arg();
//! }
//! ```
//!
//! All implementors of [command_line::flag::Flag] automatically
//! implement [command_line::ToArgs].
//!
//! If all fields of a group implement [command_line::ToArgs],
//! [command_line::ToArgs] can be `derive`d for the entire Group using
//! [runix_derive::ToArgs].
//!
//! runix ships with an initial set of commands,
//! which is bound to grow over time.
//! You can implement commands that are not yet supported in runix
//! in your own project,
//! by defining a type and implementing [command_line::NixCliCommand].
//!
//! You are welcome to contribute those implemntations back to runix!
//!
//! # Future Roadmap
//!
//! We plan to expand the command line backend with more commands and
//! a comprehensive set of flags.
//! Depending on the state of abstractions in Nix,
//! we plan to approach native bindings to Nix commands and concepts.
use Error;
/// Rust abstraction over the nix command line
/// Candidate for a standalone library to build arbitrary Nix commands in a safe manner
use NixArgs;
use async_trait;
pub use command_line as default;
use Value;
/// Marker trait for Nix Backends
///
/// [Run], [RunJson] and [RunTyped] require a [NixBackend].
/// This may be used in the future for cross backend compatibility.
/// Core trait of runix
///
/// Implemented for commands that may take a reference of a [NixBackend]
/// to run a nix command.
///
/// # Example
///
/// Following example implements [Run] for a `Command`
/// on the `MyBackend` backend.
///
/// ```no_run
/// # use runix::{NixBackend, Run};
/// # use runix::arguments::NixArgs;
/// # use runix::command_line::NixCliCommand;
/// # use runix::command::Build;
/// # use std::io;
///
/// struct MyBackend;
/// struct Command;
/// impl NixBackend for MyBackend {}
///
/// #[async_trait::async_trait]
/// impl Run<MyBackend> for Command {
/// type Error = io::Error;
///
/// async fn run(&self, _backend: &MyBackend, _nix_args: &NixArgs) -> Result<(), io::Error> {
/// panic!("42")
/// // backend.run_in_nix(args)
/// }
/// }
///
/// #[tokio::main]
/// async fn main() {
/// Command
/// .run(&MyBackend, &NixArgs {
/// ..Default::default()
/// })
/// .await
/// .unwrap()
/// }
/// ```
/// Specialized version of [Run] that guarantees JSON output
/// Specialized version of [Run] that guarantees an associated type as output