Skip to main content

pistonite_cu_proc_macros/
lib.rs

1use pm::pre::*;
2
3/// **For the Command Line Interface feature set,
4/// please refer to the [`cu::cli`](../pistonite-cu/cli/index.html) module.**
5///
6/// This is the documentation for the `#[cu::cli]` macro.
7///
8/// By annotating the main function, this macro generates
9/// a shim that will reference the `cu::cli::Flags` command line
10/// arguments and initialize logging, printing, and prompting
11/// systems accordingly.
12///
13/// The main function can be async or sync. It should
14/// return a `cu::Result`
15/// ```rust,ignore
16/// #[cu::cli]
17/// fn main(flags: cu::cli::Flags) -> cu::Result<()> {
18///     cu::debug!("flags are {flags:?}");
19///     Ok(())
20/// }
21/// ```
22///
23/// To also define your own flags using the [`clap`](https://docs.rs/clap)
24/// crate, define a CLI struct that derives `clap::Parser`.
25/// Note the prelude import (`cu::pre::*`) automatically
26/// brings `clap` into scope. You don't even need to add it
27/// to `Cargo.toml`!
28///
29/// Make sure to `#[clap(flatten)]` the flags into your struct.
30///
31/// ```rust,ignore
32/// # use pistonite_cu as cu;
33/// use cu::pre::*;
34/// /// My program
35/// ///
36/// /// This is my program, it is very good.
37/// #[derive(clap::Parser, Clone)]
38/// struct Args {
39///     /// Input of the program
40///     #[clap(short, long)]
41///     input: String,
42///     /// Output of the program
43///     #[clap(short, long)]
44///     output: Option<String>,
45///     #[clap(flatten)]
46///     inner: cu::cli::Flags,
47/// }
48/// ```
49/// Now, to tell `cu` where to look for the flags,
50/// specify the name of the field with `flags = "field"`
51/// ```rust,ignore
52/// // use the flags attribute to refer to the cu::cli::Flags field inside the Args struct
53/// #[cu::cli(flags = "inner")]
54/// fn main(args: Args) -> cu::Result<()> {
55///     cu::info!("input is {}", args.input);
56///     cu::info!("output is {:?}", args.output);
57///     Ok(())
58/// }
59/// ```
60///
61/// Alternatively, implement `AsRef<cu::cli::Flag>` for your struct.
62///
63/// ```rust,ignore
64/// # use pistonite_cu as cu;
65/// use cu::pre::*;
66/// #[derive(clap::Parser, Clone)]
67/// struct Args {
68///     input: String,
69///     #[clap(flatten)]
70///     inner: cu::cli::Flags,
71/// }
72/// impl AsRef<cu::cli::Flags> for Args {
73///     fn as_ref(&self) -> cu::cli::Flags {
74///         &self.inner
75///     }
76/// }
77/// #[cu::cli]
78/// fn main(_: Args) -> cu::Result<()> {
79///     Ok(())
80/// }
81/// ```
82///
83/// Or enable the `derive` feature and derive `AsRef` (via [`derive_more`](https://docs.rs/derive_more)).
84/// ```rust,ignore
85/// # use pistonite_cu as cu;
86/// use cu::pre::*;
87/// #[derive(clap::Parser, Clone, AsRef)]
88/// struct Args {
89///     input: String,
90///     #[clap(flatten)]
91///     #[as_ref]
92///     inner: cu::cli::Flags,
93/// }
94/// #[cu::cli]
95/// fn main(_: Args) -> cu::Result<()> {
96///     Ok(())
97/// }
98/// ```
99///
100/// The attribute can also take a `preprocess` function
101/// to process flags before initializing the CLI system.
102/// This can be useful to merge multiple Flags instance
103/// in the CLI. Note that the logging/printing system
104/// will not work during the preprocess.
105///
106/// ```rust,ignore
107/// # use pistonite_cu as cu;
108/// use cu::pre::*;
109///
110/// #[derive(clap::Parser)]
111/// struct Args {
112///     #[clap(subcommand)]
113///     subcommand: Option<Command>,
114///     #[clap(flatten)]
115///     inner: cu::cli::Flags,
116/// }
117/// impl Args {
118///     fn preprocess(&mut self) {
119///         // merge subcommand flags into top level flags
120///         // this way, both `-v foo` and `foo -v` will work
121///         if let Some(Command::Foo(c)) = &self.subcommand {
122///             self.inner.merge(c);
123///         }
124///     }
125/// }
126/// impl AsRef<cu::cli::Flags> for Args {
127///     fn as_ref(&self) -> &cu::cli::Flags {
128///         &self.inner
129///     }
130/// }
131/// #[derive(clap::Subcommand)]
132/// enum Command {
133///     Foo(cu::cli::Flags),
134/// }
135/// #[cu::cli(preprocess = Args::preprocess)]
136/// fn main(args: Args) -> cu::Result<()> {
137///     Ok(())
138/// }
139/// ```
140///
141#[proc_macro_attribute]
142pub fn cli(attr: TokenStream, input: TokenStream) -> TokenStream {
143    pm::flatten(cli::expand(attr, input))
144}
145mod cli;
146
147/// Derive the [`cu::Parse`](../pistonite-cu/trait.Parse.html) trait
148#[proc_macro_derive(Parse)]
149pub fn derive_parse(input: TokenStream) -> TokenStream {
150    pm::flatten(derive_parse::expand(input))
151}
152mod derive_parse;
153
154/// Attribute macro for wrapping a function with an error context
155///
156/// See the [tests](https://github.com/Pistonite/cu/blob/main/packages/copper/tests/context.rs)
157/// for examples
158#[proc_macro_attribute]
159pub fn context(attr: TokenStream, input: TokenStream) -> TokenStream {
160    pm::flatten(context::expand(attr, input))
161}
162mod context;