zephyrus_macros/lib.rs
1use proc_macro::TokenStream;
2
3use proc_macro2::TokenStream as TokenStream2;
4
5mod after;
6mod autocomplete;
7mod before;
8mod check;
9mod extractors;
10mod command;
11mod error_handler;
12mod hook;
13mod modal;
14mod optional;
15mod parse;
16mod util;
17
18/// Converts an `async` function into a normal function returning a
19/// `Pin<Box<dyn Future<Output = _> + '_>>`
20#[doc(hidden)]
21#[proc_macro_attribute]
22pub fn hook(_: TokenStream, input: TokenStream) -> TokenStream {
23 extract(hook::hook(input.into()))
24}
25
26/// Converts an `async-compatible` function into a builder and modifies function's body
27/// to parse all required arguments, for further information about the behaviour of this macro, see
28/// the implementation.
29///
30/// By an `async-compatible` function it's meant a function with a minimum of one argument,
31/// which must be an `&SlashContext<T>`, which is always given and also used to parse all arguments.
32///
33/// # Usage:
34///
35/// This macro can be used two ways:
36///
37/// - Without arguments, as #[command], which takes the caller function name as the name of the command.
38/// - Providing the name, as #[command("command name")] which takes the provided name as the command name.
39///
40/// When marking a function with this attribute macro, you **must** provide a description of the
41/// command that will be seen on discord when using the command, this is made by adding a
42/// `description` attribute, which can be added two ways:
43///
44/// - List way: #[description("Some description")]
45///
46/// - Named value way: #[description = "Some description"]
47///
48/// ## Arguments:
49///
50/// You **must** provide another `description` attribute for every argument describing what they
51/// are, this description will be seen on discord when filling up the argument. This needs to be
52/// done with all the arguments except the context, which must be the first one, the accepted
53/// syntax is the same as the previous `description` one.
54///
55/// ### Renaming:
56/// Adding a `rename` attribute is optional, but can be used to modify the name of the argument seen
57/// in discord, it is allowed to have only one `rename` attribute per argument and the attribute can
58/// be used the same ways a the `description` one.
59///
60/// ### Autocompletion:
61/// Adding an `autocomplete` attribute is also optional, but it allows the developer to complete
62/// the user's input for an argument. This attribute is used the same way as the description one,
63/// but it *must* point to a function marked with the `#[autocomplete]` attribute macro.
64///
65/// ## Specifying required permissions
66///
67/// It is possible to specify the permissions needed to execute the command by using the
68/// `#[required_permissions]` attribute. It accepts as input a list of comma separated
69/// [twilight permissions](https://docs.rs/twilight-model/latest/twilight_model/guild/struct.Permissions.html).
70/// For example, to specify that a user needs to have administrator permissions to execute a command,
71/// the attribute would be used like this `#[required_permissions(ADMINISTRATOR)]`.
72#[proc_macro_attribute]
73pub fn command(attrs: TokenStream, input: TokenStream) -> TokenStream {
74 extract(command::command(attrs.into(), input.into()))
75}
76
77/// Prepares the function to allow it to be set as an after hook, see
78/// the implementation for more information about this macro's behaviour.
79#[proc_macro_attribute]
80pub fn after(_: TokenStream, input: TokenStream) -> TokenStream {
81 extract(after::after(input.into()))
82}
83
84/// Prepares the function to allow it to be set as a before hook, see
85/// the implementation for more information about this macro's behaviour.
86#[proc_macro_attribute]
87pub fn before(_: TokenStream, input: TokenStream) -> TokenStream {
88 extract(before::before(input.into()))
89}
90
91#[proc_macro_attribute]
92pub fn check(_: TokenStream, input: TokenStream) -> TokenStream {
93 extract(check::check(input.into()))
94}
95
96#[proc_macro_attribute]
97pub fn error_handler(_: TokenStream, input: TokenStream) -> TokenStream {
98 extract(error_handler::error_handler(input.into()))
99}
100
101/// Prepares the function to be used to autocomplete command arguments.
102#[proc_macro_attribute]
103pub fn autocomplete(_: TokenStream, input: TokenStream) -> TokenStream {
104 extract(autocomplete::autocomplete(input.into()))
105}
106
107
108/// Implements `Parse` for an enum, allowing it to be used as a command argument.
109/// The enum will be seen by the user as a list of options to choose from, and one of the variants
110/// will have to be selected.
111///
112/// # Examples:
113///
114/// ```rust
115/// use zephyrus::prelude::*;
116///
117/// #[derive(Parse)]
118/// enum Choices {
119/// First,
120/// Second,
121/// Third,
122/// // ...
123/// }
124/// ```
125///
126/// Enums variants can be renamed to modify the value seen by the user using the
127/// `#[parse(rename = "<RENAME>")]` attribute.
128///
129/// # Example:
130/// ```rust
131/// use zephyrus::prelude::*;
132///
133/// #[derive(Parse)]
134/// enum Choices {
135/// First,
136/// Second,
137/// #[parse(rename = "Forth")]
138/// Third, // <- This item will be shown to the user as "Forth", despite its variant name being Third
139/// // ...
140/// }
141/// ```
142///
143#[proc_macro_derive(Parse, attributes(parse))]
144pub fn parse(input: TokenStream) -> TokenStream {
145 extract(parse::parse(input.into()))
146}
147
148/// Implements the `Modal` trait for the derived struct, allowing it to create modals and collect
149/// the inputs provided by the user.
150///
151/// # Examples
152///
153/// ```rust
154/// use zephyrus::prelude::*;
155///
156/// #[derive(Modal)]
157/// struct MyModal {
158/// something: String,
159/// optional_item: Option<String>
160/// }
161/// ```
162///
163/// # Attributes
164///
165/// The derive macro accepts several attributes:
166///
167/// - `#[modal(title = "<TITLE>")]`: This attribute allows specifying the title of the modal, by default the
168/// title will be the name of the structure.
169///
170/// ## Example
171///
172/// ```rust
173/// #[derive(Modal)]
174/// struct MyModal { // <- This modal will have "MyModal" as title.
175/// //...
176/// }
177///
178/// #[derive(Modal)]
179/// #[modal(title = "Some incredible modal")]
180/// struct OtherModal { // <- This one will have "Some incredible modal" as the title.
181/// // ...
182/// }
183/// ```
184///
185/// - `#[modal(label = "<LABEL>")]`: This attribute allows setting the label of the field, by default it will
186/// be the name of the struct field.
187///
188/// ## Example
189///
190/// ```rust
191/// use zephyrus::prelude::*;
192///
193/// #[derive(Modal)]
194/// struct MyModal {
195/// #[modal(label = "My field")]
196/// something: String, // <- This field will be shown as "My field"
197/// optional_item: Option<String> // <- This one will use the struct name "optional_item"
198/// }
199/// ```
200///
201/// - `#[modal(max_length = x)]` and `#[modal(min_length = y)]`: These attributes allow to set a
202/// maximum/minimum amount of characters a field can have.
203///
204/// ## Example
205///
206/// ```rust
207/// use zephyrus::prelude::*;
208///
209/// #[derive(Modal)]
210/// struct MyModal {
211/// #[modal(max_length = 150, min_length = 15)]
212/// something: String, // <- This field will have both maximum and minimum size constraints.
213/// #[modal(max_length) = 25]
214/// short_field: String, // <- This field will only have a maximum size constraint.
215/// optional_item: Option<String> // <- This one won't have any
216/// }
217/// ```
218///
219/// - #[modal(placeholder = "<PLACEHOLDER>")]: This attribute allows specifying a placeholder that will be seen
220/// before entering anything on the input.
221///
222/// ## Example
223///
224/// ```rust
225/// use zephyrus::prelude::*;
226///
227/// #[derive(Modal)]
228/// struct MyModal {
229/// #[modal(placeholder = "This is a placeholder")]
230/// something: String, // <- This field will have as placeholder "This is a placeholder".
231/// }
232/// ```
233///
234/// - `#[modal(paragraph)]`: This attribute will mark the field as a paragraph. By default, all fields are
235/// marked as single line fields, so the user will only be able to input up to one line unless we
236/// mark it as a paragraph.
237///
238/// ## Example
239///
240/// ```rust
241/// use zephyrus::prelude::*;
242///
243/// #[derive(Modal)]
244/// struct MyModal {
245/// #[modal(paragraph)]
246/// something: String, // <- This field will be shown as a multi-line field.
247/// optional_item: Option<String> // <- This one will be shown as a single line one.
248/// }
249/// ```
250#[proc_macro_derive(
251 Modal,
252 attributes(modal)
253)]
254pub fn modal(input: TokenStream) -> TokenStream {
255 extract(modal::modal(input.into()))
256}
257
258/// Extracts the given result, throwing a compile error if an error is given.
259fn extract(res: syn::Result<TokenStream2>) -> TokenStream {
260 match res {
261 Ok(s) => s,
262 Err(why) => why.to_compile_error(),
263 }
264 .into()
265}