Skip to main content

cruiser_derive/
lib.rs

1#![warn(unused_import_braces, unused_imports, missing_docs, clippy::pedantic)]
2#![allow(clippy::similar_names, clippy::module_name_repetitions)]
3
4//! The proc macros of [`cruiser`](https://docs.rs/cruiser/latest/cruiser/)
5
6extern crate proc_macro;
7
8use proc_macro::TokenStream;
9
10#[cfg(feature = "easy_proc_test")]
11use proc_macro2::Span;
12use proc_macro_crate::{crate_name, FoundCrate};
13use proc_macro_error::proc_macro_error;
14use quote::{format_ident, quote};
15#[cfg(feature = "easy_proc_test")]
16use syn::parse::{Parse, ParseStream};
17use syn::parse_macro_input;
18#[cfg(feature = "easy_proc_test")]
19use syn::{Ident, LitInt, LitStr};
20
21#[cfg(feature = "easy_proc_test")]
22use easy_proc::ArgumentList;
23
24use crate::account_argument::AccountArgumentDerive;
25use crate::account_list::AccountListDerive;
26use crate::error::ErrorDerive;
27use crate::instruction_list::InstructionListDerive;
28use crate::verify_account_arg_impl::VerifyAccountArgs;
29
30mod account_argument;
31mod account_list;
32mod error;
33mod in_place;
34mod instruction_list;
35mod log_level;
36mod verify_account_arg_impl;
37
38/// If no start specified starts at `1_000_000`
39#[proc_macro_error]
40#[proc_macro_derive(Error, attributes(error, error_msg))]
41pub fn derive_error(ts: TokenStream) -> TokenStream {
42    let stream = parse_macro_input!(ts as ErrorDerive).into_token_stream();
43    #[cfg(feature = "debug_error")]
44    {
45        println!("{}", stream);
46        std::thread::sleep(std::time::Duration::from_millis(100));
47    }
48    stream.into()
49}
50
51/// Derives `AccountArgument`, `FromAccounts`, and `ValidateArgument`.
52///
53/// # Requirements
54/// This macro is implemented for structs only. Each field must implement `AccountArgument`.
55///
56/// # How to use
57/// This macro utilizes `from`, `validate`, and `account_argument` attributes on the struct, and `from` and `validate` on the fields.
58///
59/// # `account_argument`
60/// Arguments for the whole struct
61/// ```ignore
62/// #[derive(AccountArgument)]
63/// #[account_argument(
64///     no_from,
65///     no_validate,
66///     enum_discriminant_type = <$ty:ty>,
67///     account_info = <$ty:ty>,
68///     generics = [$(<$($gen:gen),*>)? $(where $($clause:where_clause),*)?],
69/// )]
70/// struct Test;
71/// ```
72/// | Argument | Argument Type | Description |
73/// |---|---|---|
74/// | `no_from` | presence | Presence of this means all `from` attributes are ignored and no default `FromAccounts` implementation is generated. |
75/// | `no_validate` | presence | Presence of this means all `validate` attributes are ignored and no default `ValidateArgument` implementation is generated. |
76/// | ~~`enum_discriminant_type = <$ty:ty>`~~ | optional | Sets the serialization type for the enum discriminant. Type must implement `CompressedNumber<Num = u64>`. Defaults to [`u64`]. Not yet implemented. |
77/// | `account_info` | required | Sets the type for this arguments accoutn info. Most library functions are writen with this as a generic but you an force it to be a specific type as well. |
78/// | `generics` | optional | Additional generics to apply to `AccountArgument`, `FromAccounts`, and `ValidateArgument` implementations. Can include generics and a where clause. |
79///
80/// # `from`
81/// Arguments for `FromAccounts` implementation. Multiple `from` attributes can exist, each with a different id.
82/// ```ignore
83/// #[derive(AccountArgument)]
84/// #[from(
85///     id = <$id:ident>,
86///     data = (<$($data_name:ident: $data_ty:ty),*>),
87///     enum_discriminant = <$dis:expr>,
88///     log_level: <$log_level:ident>,
89///     generics = [$(<$($gen:gen),*>)? $(where $($clause:where_clause),*)?],
90/// )]
91/// struct Test{
92///     #[from(
93///         id = <$id:ident>,
94///         data = <$data:expr>,
95///     )]
96///     field: FieldType,
97/// }
98/// ```
99///
100/// ## Struct Attribute
101/// | Argument | Argument Type | Description |
102/// |---|---|---|
103/// | `id = <$id:ident>` | optional | Sets the id for this attribute and for other to reference. Defaults to unique default id. |
104/// | `data = (<$($data_name:ident: $data_ty:ty),*>)` | optional | Data type coming in for the `FromAccounts` implementation. `$data_name` is the name that can be referenced. `$data_ty` is the type of the data argument. Type defaults to [`()`] and maps to a tupple of the types. If a single argument is present then both `FromAccounts<$data_ty>` and `FromAccounts<($data_ty,)>` are implemented. |
105/// | ~~`enum_discriminant = <$dis:expr>`~~ | optional | Sets the enum discriminant from the incoming data. Required if deriving on enum. Not yet implemented. |
106/// | `log_level = $<log_level:ident>` | optional | Sets the logging level for implementation. Valid are `none`, `error`, `warn`, `info`, `debug`, or `trace` |
107/// | `generics = [$(<$($gen:gen),*>)? $(where $($clause:where_clause),*)?]` | optional | Additional generics to apply to this `FromAccounts` implementation. Can include generics and a where clause. |
108///
109/// ## Field Attribute
110/// | Argument | Argument Type | Description |
111/// |---|---|---|
112/// | `id = <$id:ident>` | optional | Points to the struct attribute that this references. Defaults to unique empty id. |
113/// | `data = <$data:expr>` | optional | The argument to pass to the field's `FromAccounts` implementation. Defaults to [`()`] |
114///
115/// # `validate`
116/// Arguments for `ValidateArgument` implementation. Multiple `validate` attributes can exist, each with a different id.
117/// ```ignore
118/// #[derive(AccountArgument)]
119/// #[validate(
120///     id = <$id:ident>,
121///     data = (<$($data_name:ident: $data_ty:ty),*>),
122///     log_level: <$log_level:ident>,
123///     generics = [$(<$($gen:gen),*>)? $(where $($clause:where_clause),*)?],
124/// )]
125/// struct Test{
126///     #[validate(
127///         id = <$id:ident>,
128///         data = <$data:expr>,
129///         signer(<$index:expr>),
130///         writable(<$index:expr>),
131///         owner(<$index:expr>) = <$owner:expr>,
132///         key(<$index:expr>) = <$key:expr>,
133///     )]
134///     field: FieldType,
135/// }
136/// ```
137/// ## Struct Attribute
138/// | Argument | Argument Type | Description |
139/// |---|---|---|
140/// | `id = <$id:ident>` | optional | Sets the id for this attribute and for other to reference. Defaults to unique default id. |
141/// | `data = (<$($data_name:ident: $data_ty:ty),*>)` | optional | Data type coming in for the `ValidateArgument` implementation. `$data_name` is the name that can be referenced. `$data_ty` is the type of the data argument. Type defaults to [`()`] and maps to a tupple of the types. If a single argument is present then both `ValidateArgument<$data_ty>` and `ValidateArgument<($data_ty,)>` are implemented. |
142/// | `log_level = $<log_level:ident>` | optional | Sets the logging level for implementation. Valid are `none`, `error`, `warn`, `info`, `debug`, or `trace` |
143/// | `generics = [$(<$($gen:gen),*>)? $(where $($clause:where_clause),*)?]` | optional | Additional generics to apply to this `ValidateArgument` implementation. Can include generics and a where clause. |
144///
145/// ## Field Attribute
146/// | Argument | Argument Type | Description |
147/// |---|---|---|
148/// | `id = <$id:ident>` | optional | Points to the struct attribute that this references. Defaults to unique empty id. |
149/// | `data = <$data:expr>` | optional | The argument to pass to the field's `ValidateArgument` implementation. Defaults to [`()`] |
150/// | `signer(<$index:expr>)` | multiple, 0+ | Checks that `MultiIndexable::is_signer($index)` is true. If indexer is omitted defaults to `AllAny::All` |
151/// | `writable(<$index:expr)` | multiple, 0+ | Checks that `MultiIndexable::is_signer($index)` is true. If indexer is omitted defaults to `AllAny::All` |
152/// | `owner(<$index:expr>) = <$owner:expr>` | multiple, 0+ | Checks that `MultiIndexable::is_owner($owner, $index)` is true. If indexer is omitted defaults to `AllAny::All` |
153/// | `key(<$index:expr) = <$key:expr>` | multiple, 0+ | Checks that `SingleIndexable::info($index).key` is `$key`. If indexer is omitted defaults to `AllAny::All` |
154#[proc_macro_error]
155#[proc_macro_derive(AccountArgument, attributes(from, account_argument, validate))]
156pub fn derive_account_argument(ts: TokenStream) -> TokenStream {
157    let stream = parse_macro_input!(ts as AccountArgumentDerive).into_token_stream();
158    #[cfg(feature = "debug_account_argument")]
159    {
160        println!("{}", stream);
161        std::thread::sleep(std::time::Duration::from_millis(100));
162    }
163    stream.into()
164}
165
166/// Derives the `InstructionList` trait.
167///
168/// TODO: Write docs for this
169#[proc_macro_error]
170#[proc_macro_derive(InstructionList, attributes(instruction_list, instruction))]
171pub fn derive_instruction_list(ts: TokenStream) -> TokenStream {
172    let stream = parse_macro_input!(ts as InstructionListDerive).into_token_stream();
173    #[cfg(feature = "debug_instruction_list")]
174    {
175        println!("{}", stream);
176        std::thread::sleep(std::time::Duration::from_millis(100));
177    }
178    stream.into()
179}
180
181/// Derives the `AccountList` trait
182///
183/// TODO: Write docs for this
184#[proc_macro_error]
185#[proc_macro_derive(AccountList)]
186pub fn derive_account_list(ts: TokenStream) -> TokenStream {
187    let stream = parse_macro_input!(ts as AccountListDerive).into_token_stream();
188    #[cfg(feature = "debug_account_list")]
189    {
190        println!("{}", stream);
191        std::thread::sleep(std::time::Duration::from_millis(100));
192    }
193    stream.into()
194}
195
196/// Verifies a given type implements the proper traits
197///
198/// TODO: Write docs for this
199#[proc_macro_error]
200#[proc_macro]
201pub fn verify_account_arg_impl(tokens: TokenStream) -> TokenStream {
202    let stream = parse_macro_input!(tokens as VerifyAccountArgs).into_token_stream();
203    stream.into()
204}
205
206fn get_crate_name() -> proc_macro2::TokenStream {
207    let generator_crate = crate_name("cruiser").expect("Could not find `cruiser`");
208    match generator_crate {
209        FoundCrate::Itself => quote! { ::cruiser },
210        FoundCrate::Name(name) => {
211            let ident = format_ident!("{}", name);
212            quote! { ::#ident }
213        }
214    }
215}
216
217/// Macro for testing `easy_proc`
218#[cfg(feature = "easy_proc_test")]
219#[proc_macro_error]
220#[proc_macro_attribute]
221pub fn test_easy_proc(args: TokenStream, tokens: TokenStream) -> TokenStream {
222    println!("ts1: {}", args);
223    println!("ts2: {}", tokens);
224
225    let tokens = parse_macro_input!(tokens as TestStruct);
226    tokens.into_token_stream()
227}
228
229#[cfg(feature = "easy_proc_test")]
230struct TestStruct {
231    cool: Cool,
232}
233#[cfg(feature = "easy_proc_test")]
234impl TestStruct {
235    fn into_token_stream(self) -> TokenStream {
236        if self.cool.boolean_value {
237            (quote::quote! {
238                fn cool(){
239                    println!("Success!");
240                }
241            })
242            .into()
243        } else {
244            proc_macro_error::abort_call_site!("Oh No!");
245        }
246    }
247}
248#[cfg(feature = "easy_proc_test")]
249impl Parse for TestStruct {
250    fn parse(input: ParseStream) -> syn::Result<Self> {
251        input.parse::<syn::Token![fn]>()?;
252        input.parse::<syn::Ident>()?;
253        let _content;
254        syn::parenthesized!(_content in input);
255        let content;
256        syn::braced!(content in input);
257        let function = content.parse::<syn::ItemFn>()?;
258        let cool = Cool::parse_arguments(&function.attrs[0]);
259        Ok(Self { cool })
260    }
261}
262
263#[cfg(feature = "easy_proc_test")]
264#[derive(ArgumentList)]
265#[allow(dead_code)]
266struct Cool {
267    /// The ident of the whole attribute, not required and can only be one
268    #[argument(attr_ident)]
269    pub attr_ident: Ident,
270    /// [`true`] if arg is present
271    #[argument(presence)]
272    pub boolean_value: bool,
273    /// Required argument of form `count = 10`
274    pub count: LitInt,
275    /// Optional argument, if present of form `size = 3`
276    pub size: Option<LitInt>,
277    /// Custom parsing, including equals. Uses parse function.
278    /// Ex: `custom_parse cool`
279    #[argument(custom)]
280    pub custom_parse: Ident,
281    /// Optional with default value. Also implies `raw_type`
282    #[argument(default = Ident::new("default", Span::call_site()))]
283    pub default: Ident,
284    /// Many, 0 or more
285    pub many: Vec<LitStr>,
286}