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
use *;
/// **For the Command Line Interface feature set,
/// please refer to the [`cu::cli`](../pistonite-cu/cli/index.html) module.**
///
/// This is the documentation for the `#[cu::cli]` macro.
///
/// By annotating the main function, this macro generates
/// a shim that will reference the `cu::cli::Flags` command line
/// arguments and initialize logging, printing, and prompting
/// systems accordingly.
///
/// The main function can be async or sync. It should
/// return a `cu::Result`
/// ```rust,ignore
/// #[cu::cli]
/// fn main(flags: cu::cli::Flags) -> cu::Result<()> {
/// cu::debug!("flags are {flags:?}");
/// Ok(())
/// }
/// ```
///
/// To also define your own flags using the [`clap`](https://docs.rs/clap)
/// crate, define a CLI struct that derives `clap::Parser`.
/// Note the prelude import (`cu::pre::*`) automatically
/// brings `clap` into scope. You don't even need to add it
/// to `Cargo.toml`!
///
/// Make sure to `#[clap(flatten)]` the flags into your struct.
///
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
/// /// My program
/// ///
/// /// This is my program, it is very good.
/// #[derive(clap::Parser, Clone)]
/// struct Args {
/// /// Input of the program
/// #[clap(short, long)]
/// input: String,
/// /// Output of the program
/// #[clap(short, long)]
/// output: Option<String>,
/// #[clap(flatten)]
/// inner: cu::cli::Flags,
/// }
/// ```
/// Now, to tell `cu` where to look for the flags,
/// specify the name of the field with `flags = "field"`
/// ```rust,ignore
/// // use the flags attribute to refer to the cu::cli::Flags field inside the Args struct
/// #[cu::cli(flags = "inner")]
/// fn main(args: Args) -> cu::Result<()> {
/// cu::info!("input is {}", args.input);
/// cu::info!("output is {:?}", args.output);
/// Ok(())
/// }
/// ```
///
/// Alternatively, implement `AsRef<cu::cli::Flag>` for your struct.
///
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
/// #[derive(clap::Parser, Clone)]
/// struct Args {
/// input: String,
/// #[clap(flatten)]
/// inner: cu::cli::Flags,
/// }
/// impl AsRef<cu::cli::Flags> for Args {
/// fn as_ref(&self) -> cu::cli::Flags {
/// &self.inner
/// }
/// }
/// #[cu::cli]
/// fn main(_: Args) -> cu::Result<()> {
/// Ok(())
/// }
/// ```
///
/// Or enable the `derive` feature and derive `AsRef` (via [`derive_more`](https://docs.rs/derive_more)).
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
/// #[derive(clap::Parser, Clone, AsRef)]
/// struct Args {
/// input: String,
/// #[clap(flatten)]
/// #[as_ref]
/// inner: cu::cli::Flags,
/// }
/// #[cu::cli]
/// fn main(_: Args) -> cu::Result<()> {
/// Ok(())
/// }
/// ```
///
/// ## Attributes
///
/// ### `preprocess`
///
/// The attribute can also take a `preprocess` function
/// to process flags before initializing the CLI system.
/// This can be useful to merge multiple Flags instance
/// in the CLI. Note that the logging/printing system
/// will not work during the preprocess.
///
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
///
/// #[derive(clap::Parser)]
/// struct Args {
/// #[clap(subcommand)]
/// subcommand: Option<Command>,
/// #[clap(flatten)]
/// inner: cu::cli::Flags,
/// }
/// impl Args {
/// fn preprocess(&mut self) {
/// // merge subcommand flags into top level flags
/// // this way, both `-v foo` and `foo -v` will work
/// if let Some(Command::Foo(c)) = &self.subcommand {
/// self.inner.merge(c);
/// }
/// }
/// }
/// impl AsRef<cu::cli::Flags> for Args {
/// fn as_ref(&self) -> &cu::cli::Flags {
/// &self.inner
/// }
/// }
/// #[derive(clap::Subcommand)]
/// enum Command {
/// Foo(cu::cli::Flags),
/// }
/// #[cu::cli(preprocess = Args::preprocess)]
/// fn main(args: Args) -> cu::Result<()> {
/// Ok(())
/// }
/// ```
///
/// ### `log_config`
/// `log_config` takes a function that returns a `LogConfig` trait implementation,
/// which can be used to alter how each `LogRecord` is displayed.
///
/// Below is an example that: overrides info messages in `some_library` to be debug messages
/// if the print level is info, and always show module path for that library.
///
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
///
/// struct LogConfig(cu::lv::PrintLevel);
/// impl LogConfig {
/// pub fn new(flags: &cu::cli::Flags) -> Self {
/// Self(flags.print_level())
/// }
/// }
/// impl cu::cli::LogConfig for LogConfig {
/// fn process(&self, record: &cu::lv::LogRecord) -> (cu::lv::Lv, bool) {
/// if self.0 == cu::lv::PrintLevel::Normal {
/// if let Some(m) = record.module_path() {
/// if m == "some_library" {
/// let level: cu::lv::Lv = record.level().into();
/// let is_info = level == cu::lv::I;
/// return (if is_info { cu::lv::D } else { level }, true);
/// }
/// }
/// }
/// cu::cli::DefaultLogConfig.process(record)
/// }
/// }
///
/// #[cu::cli(log_config = LogConfig::new)]
/// fn main(_: cu::cli::Flags) -> cu::Result<()> {
/// Ok(())
/// }
/// ```
/// Derive the [`cu::Parse`](../pistonite-cu/trait.Parse.html) trait
/// Attribute macro for wrapping a function with an error context
///
/// See the [tests](https://github.com/Pistonite/cu/blob/main/packages/copper/tests/context.rs)
/// for examples