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}