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
//! Dispatch: handing a parsed command to the code that carries it out.
//!
//! A parse ends with a value — an enum whose selected variant holds the command's own
//! struct — and every CLI then writes the same thing: a `match` over that enum, one arm per
//! command, each arm calling the one function that command exists to call. At mise's size
//! that match is 210 arms of pure routing, and the compiler cannot tell that an arm calling
//! the wrong function is wrong, because every arm has the same shape.
//!
//! So the derive writes it. A command implements [`Run`] (or [`RunWith`], when the CLI hands
//! its commands shared state), the enum says `#[usage(run)]`, and the match is generated from
//! the same declaration the parser and the spec come from. Nothing about it reaches the
//! spec: which Rust function carries out a command is not part of what the CLI *is*, and a
//! spec that recorded it could not be read by anything that is not this program. It is the
//! same rule `#[usage(skip)]` follows.
//!
//! Every one of these traits takes `self` by value. A command is finished when it has run, and
//! the values it parsed are its own — taking them by reference would mean every handler
//! borrowing what nothing else can want.
//!
//! # Which one
//!
//! | | no context | a context |
//! | -------------- | ---------- | -------------------- |
//! | **sync** | [`Run`] | [`RunWith`] |
//! | **async** | [`RunAsync`] | [`RunAsyncWith`] |
//!
//! The context is whatever the CLI has to give — a resolved config, an output handle, a
//! database connection — and the `With` traits are generic over it, so `RunWith<&mut App>` and
//! `RunAsyncWith<Arc<Ctx>>` are ordinary implementations rather than shapes this crate has to
//! anticipate.
//!
//! A context is a separate trait rather than one defaulted to `()` because the noise otherwise
//! falls on the wrong side of a CLI: a hundred commands that need no context would each carry
//! `fn run(self, _: ())`, which says nothing and cannot be left out. One type may implement
//! several of these, and one enum may dispatch several, which is what a CLI part-way through
//! adopting a context — or an async runtime — needs.
//!
//! # Async commands
//!
//! [`RunAsync`] and [`RunAsyncWith`] are the async pair: an implementation writes `async fn`,
//! and the generated dispatch is an `async fn` that awaits the selected command.
//!
//! ```
//! use usage_argv::RunAsync;
//!
//! struct Install {
//! force: bool,
//! }
//!
//! impl RunAsync for Install {
//! type Output = Result<(), String>;
//! async fn run_async(self) -> Self::Output {
//! // .await here
//! Ok(())
//! }
//! }
//! ```
//!
//! The trait declares `-> impl Future<Output = Self::Output>` rather than `async fn`, which is
//! the same thing on the implementing side and **deliberately imposes no `Send` bound**: a CLI
//! on a single-threaded runtime keeps futures that hold an `Rc` across an await, and one that
//! spawns gets `Send` by inference, since it leaks out of the concrete commands the dispatch
//! reaches. What this cannot do is *demand* `Send` in generic code, which is the trade the
//! alternative — `-> impl Future + Send` in the trait — makes in the other direction, and
//! there is no way to have both without duplicating the trait.
//!
//! The sync pair can carry a future too, since [`Output`](Run::Output) is whatever the command
//! produces: a boxed `Pin<Box<dyn Future<Output = T>>>` (plus `+ Send` if the CLI wants it) is
//! a value like any other. That costs an allocation and names a type; the async traits exist so
//! that neither is necessary.
//!
//! # An example
//!
//! ```
//! use usage_argv::Run;
//!
//! struct Install {
//! force: bool,
//! }
//! struct Sponsors;
//!
//! impl Run for Install {
//! type Output = Result<(), String>;
//! fn run(self) -> Self::Output {
//! if self.force {
//! Ok(())
//! } else {
//! Err("refusing without --force".into())
//! }
//! }
//! }
//!
//! impl Run for Sponsors {
//! type Output = Result<(), String>;
//! fn run(self) -> Self::Output {
//! println!("thanks");
//! Ok(())
//! }
//! }
//!
//! // What `#[usage(run)]` on the subcommand enum generates, written out.
//! enum Command {
//! Install(Install),
//! Sponsors(Sponsors),
//! }
//!
//! impl Run for Command
//! where
//! Install: Run,
//! Sponsors: Run<Output = <Install as Run>::Output>,
//! {
//! type Output = <Install as Run>::Output;
//! fn run(self) -> Self::Output {
//! match self {
//! Command::Install(inner) => Run::run(inner),
//! Command::Sponsors(inner) => Run::run(inner),
//! }
//! }
//! }
//!
//! assert!(Command::Install(Install { force: true }).run().is_ok());
//! ```
/// A command that can be carried out with nothing but what it parsed.
///
/// The output is the implementation's own: `Result<(), E>` for a CLI whose commands can
/// fail, `()` for one whose commands cannot, [`ExitCode`](std::process::ExitCode) for one
/// that decides its own status. A generated dispatcher takes its output from the first
/// command it routes to and requires the rest to agree, since a `match` has one type.
/// A command that is handed something shared when it runs.
///
/// `Ctx` is whatever the CLI has to give: `&Config`, `&mut App`, an owned handle. It is a
/// parameter of the trait rather than of the method so that one command may be runnable with
/// several — a leaf that needs only a config can implement `RunWith<&Config>` while its
/// siblings implement `RunWith<&mut App>`, as long as the enum dispatching them agrees on
/// one.
/// An async command: [`Run`], awaited.
///
/// The signature is `-> impl Future` rather than `async fn` so that no `Send` bound is implied
/// either way — an implementation still writes `async fn run_async(self)`, and whether its
/// future is `Send` is decided by what the command does rather than by this trait. See the
/// [module docs](self#async-commands).
/// An async command that is handed something shared when it runs: [`RunWith`], awaited.
///
/// A borrowed context is the ordinary case, and the future borrows it for as long as it runs:
/// `impl<'a> RunAsyncWith<&'a App> for Install`.