synthez_codegen/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(
3    macro_use_extern_crate,
4    nonstandard_style,
5    rust_2018_idioms,
6    rustdoc::all,
7    trivial_casts,
8    trivial_numeric_casts
9)]
10#![forbid(non_ascii_idents, unsafe_code)]
11#![warn(
12    clippy::as_conversions,
13    clippy::as_ptr_cast_mut,
14    clippy::assertions_on_result_states,
15    clippy::branches_sharing_code,
16    clippy::clone_on_ref_ptr,
17    clippy::create_dir,
18    clippy::dbg_macro,
19    clippy::debug_assert_with_mut_call,
20    clippy::decimal_literal_representation,
21    clippy::default_union_representation,
22    clippy::derive_partial_eq_without_eq,
23    clippy::else_if_without_else,
24    clippy::empty_drop,
25    clippy::empty_line_after_outer_attr,
26    clippy::empty_structs_with_brackets,
27    clippy::equatable_if_let,
28    clippy::exit,
29    clippy::expect_used,
30    clippy::fallible_impl_from,
31    clippy::filetype_is_file,
32    clippy::float_cmp_const,
33    clippy::fn_to_numeric_cast,
34    clippy::fn_to_numeric_cast_any,
35    clippy::format_push_string,
36    clippy::get_unwrap,
37    clippy::if_then_some_else_none,
38    clippy::imprecise_flops,
39    clippy::index_refutable_slice,
40    clippy::iter_on_empty_collections,
41    clippy::iter_on_single_items,
42    clippy::iter_with_drain,
43    clippy::large_include_file,
44    clippy::let_underscore_untyped,
45    clippy::lossy_float_literal,
46    clippy::manual_clamp,
47    clippy::map_err_ignore,
48    clippy::mem_forget,
49    clippy::missing_const_for_fn,
50    clippy::missing_docs_in_private_items,
51    clippy::multiple_inherent_impl,
52    clippy::multiple_unsafe_ops_per_block,
53    clippy::mutex_atomic,
54    clippy::mutex_integer,
55    clippy::needless_collect,
56    clippy::nonstandard_macro_braces,
57    clippy::option_if_let_else,
58    clippy::or_fun_call,
59    clippy::panic_in_result_fn,
60    clippy::partial_pub_fields,
61    clippy::pedantic,
62    clippy::print_stderr,
63    clippy::print_stdout,
64    clippy::rc_buffer,
65    clippy::rc_mutex,
66    clippy::rest_pat_in_fully_bound_structs,
67    clippy::same_name_method,
68    clippy::semicolon_inside_block,
69    clippy::shadow_unrelated,
70    clippy::significant_drop_in_scrutinee,
71    clippy::significant_drop_tightening,
72    clippy::str_to_string,
73    clippy::string_add,
74    clippy::string_lit_as_bytes,
75    clippy::string_slice,
76    clippy::string_to_string,
77    clippy::suboptimal_flops,
78    clippy::suspicious_operation_groupings,
79    clippy::suspicious_xor_used_as_pow,
80    clippy::todo,
81    clippy::trailing_empty_array,
82    clippy::transmute_undefined_repr,
83    clippy::trivial_regex,
84    clippy::try_err,
85    clippy::undocumented_unsafe_blocks,
86    clippy::unimplemented,
87    clippy::unnecessary_safety_comment,
88    clippy::unnecessary_safety_doc,
89    clippy::unnecessary_self_imports,
90    clippy::unneeded_field_pattern,
91    clippy::unused_peekable,
92    clippy::unwrap_in_result,
93    clippy::unwrap_used,
94    clippy::use_debug,
95    clippy::use_self,
96    clippy::useless_let_if_seq,
97    clippy::verbose_file_reads,
98    clippy::wildcard_enum_match_arm,
99    future_incompatible,
100    let_underscore_drop,
101    meta_variable_misuse,
102    missing_copy_implementations,
103    missing_debug_implementations,
104    missing_docs,
105    noop_method_call,
106    semicolon_in_expressions_from_macros,
107    unreachable_pub,
108    unused_crate_dependencies,
109    unused_extern_crates,
110    unused_import_braces,
111    unused_labels,
112    unused_lifetimes,
113    unused_qualifications,
114    unused_results,
115    unused_tuple_struct_fields,
116    variant_size_differences
117)]
118
119use proc_macro::TokenStream;
120use synthez_core::codegen;
121
122// TODO: Remove once tests run without complains about it.
123#[cfg(test)]
124use proc_macro2 as _; // for docs only
125#[cfg(test)]
126use synthez as _; // for docs only
127
128/// Deriving of [`synthez::ParseAttrs`] along with a [`syn::parse::Parse`]
129/// implementation to parse [`syn::Attribute`]s into a custom defined struct.
130///
131/// # Field requirements
132///
133/// Each field should be wrapped into a [`field::Container`] implementor, which
134/// describes and influences the parsing logic. Use [`Required`]
135/// [`field::Container`] in case your parsing logic demands mandatory specifying
136/// of a value.
137///
138/// Type of the parsed valued (the one contained in a [`field::Container`]) must
139/// implement [`Parse`] and [`Spanned`] (vital for compile-time error
140/// reporting). You may use the [`Spanning`] wrapper in case it doesn't
141/// implement the latest.
142///
143/// # Arguments
144///
145/// ## `ident`, `value`, `map` or `nested` (mandatory)
146///
147/// Defines kind of parsing for a struct field.
148///
149/// ```rust
150/// # use std::collections::{HashMap, HashSet};
151/// #
152/// # use syn::parse_quote;
153/// # use synthez::{ParseAttrs, Spanning};
154/// #
155/// #[derive(Debug, Default, ParseAttrs, PartialEq)]
156/// struct MyAttrs {
157///     /// Will parse only `#[my_attr(ident)]`.
158///     #[parse(ident)]
159///     ident: Option<syn::Ident>,
160///
161///     /// Will parse `#[my_attr(value = <expr>)]`, `#[my_attr(value(<expr>))]`
162///     /// and `#[my_attr(value(<expr1>, <expr2>))]`.
163///     #[parse(value)]
164///     value: Vec<syn::Expr>,
165///
166///     /// Will parse `#[my_attr(value <lit>)]`, `#[my_attr(value(<lit>))]`
167///     /// and `#[my_attr(value(<lit1>, <lit2>))]`.
168///     #[parse(value(spaced))]
169///     value_spaced: HashSet<syn::Lit>,
170///
171///     /// Will parse `#[my_attr(map <ident> = <type>)]` only.
172///     #[parse(map)]
173///     map: HashMap<syn::Ident, syn::Type>,
174///
175///     /// Will parse `#[my_attr(nested(<arg1>, <arg2>))]` only.
176///     ///
177///     /// Note, we use [`Box`] here only because of recursive structure.
178///     #[parse(nested)]
179///     nested: Option<Spanning<Box<MyAttrs>>>,
180/// }
181///
182/// # fn main() {
183/// let input: syn::DeriveInput = parse_quote! {
184///     #[my_attr(ident)]
185///     #[my_attr(value = 2 * 2, value_spaced "some")]
186///     #[my_attr(map A = Option<u8>)]
187///     #[my_attr(map B = syn::Result<()>)]
188///     #[my_attr(nested(ident, value = "another"))]
189///     struct Dummy;
190/// };
191/// let my_attrs = MyAttrs::parse_attrs("my_attr", &input);
192///
193/// let expected_nested = MyAttrs {
194///     ident: Some(parse_quote!(ident)),
195///     value: vec![parse_quote!("another")],
196///     ..MyAttrs::default()
197/// };
198///
199/// assert!(my_attrs.is_ok());
200/// # let my_attrs = my_attrs.unwrap();
201/// assert_eq!(my_attrs.ident, Some(parse_quote!(ident)));
202/// assert_eq!(my_attrs.value, vec![parse_quote!(2 * 2)]);
203/// assert!(my_attrs.value_spaced.contains(&parse_quote!("some")));
204/// assert_eq!(my_attrs.map.len(), 2);
205/// assert_eq!(my_attrs.map[&parse_quote!(A)], parse_quote!(Option<u8>));
206/// assert_eq!(my_attrs.map[&parse_quote!(B)], parse_quote!(syn::Result<()>));
207/// assert_eq!(*my_attrs.nested.unwrap().into_inner(), expected_nested);
208/// # }
209/// ```
210///
211/// Only one such argument can be chosen for a single field.
212///
213/// ```rust,compile_fail
214/// # use synthez::ParseAttrs;
215/// #
216/// #[derive(Default, ParseAttrs)]
217/// struct Wrong {
218///     /// We cannot use two kinds of parsing simultaneously.
219///     #[parse(ident, value)]
220///     field: Option<syn::Ident>,
221/// }
222/// ```
223///
224/// ## `alias = <name>`, `aliases(<name1>, <name2>)` (optional)
225///
226/// Adds aliases for an attribute's argument in addition to its field ident.
227///
228/// ```rust
229/// # use syn::parse_quote;
230/// # use synthez::ParseAttrs;
231/// #
232/// #[derive(Default, ParseAttrs)]
233/// struct MyAttrs {
234///     #[parse(value, alias = value)]
235///     #[parse(aliases(vals, values))]
236///     val: Vec<syn::Lit>,
237/// }
238///
239/// # fn main() {
240/// let input: syn::DeriveInput = parse_quote! {
241///     #[my_attr(val = "foo")]
242///     #[my_attr(value = "bar")]
243///     #[my_attr(vals(1, 2), values(3, 4))]
244///     struct Dummy;
245/// };
246/// let my_attrs = MyAttrs::parse_attrs("my_attr", &input);
247///
248/// # assert!(my_attrs.is_ok());
249/// # let my_attrs = my_attrs.unwrap();
250/// assert_eq!(my_attrs.val.len(), 6);
251/// # }
252/// ```
253///
254/// ## `arg = <name>`, `args(<name1>, <name2>)` (optional)
255///
256/// Similar to `alias` argument, but excludes the field ident from possible
257/// names of a parsed attribute's argument. Can be used with `alias` argument
258/// simultaneously.
259///
260/// ```rust
261/// # use syn::parse_quote;
262/// # use synthez::ParseAttrs;
263/// #
264/// #[derive(Default, ParseAttrs)]
265/// struct MyAttrs {
266///     #[parse(value, arg = value)]
267///     #[parse(args(vals, values))]
268///     #[parse(alias = v_a_l)]
269///     val: Vec<syn::Lit>,
270/// }
271///
272/// # fn main() {
273/// let input: syn::DeriveInput = parse_quote! {
274///     #[my_attr(value = "foo")]
275///     #[my_attr(vals(1, 2), values(3, 4))]
276///     #[my_attr(v_a_l = "bar")]
277///     struct Dummy;
278/// };
279/// let my_attrs = MyAttrs::parse_attrs("my_attr", &input);
280///
281/// # assert!(my_attrs.is_ok());
282/// # let my_attrs = my_attrs.unwrap();
283/// assert_eq!(my_attrs.val.len(), 6);
284///
285/// let wrong: syn::DeriveInput = parse_quote! {
286///     #[my_attr(val = "foo")]
287///     struct Dummy;
288/// };
289/// let my_attrs = MyAttrs::parse_attrs("my_attr", &wrong);
290///
291/// assert!(my_attrs.is_err());
292/// # }
293/// ```
294///
295/// ## `dedup = <strategy>` (optional)
296///
297/// Defines deduplication strategy for the repeated same values during parsing.
298/// Can be one of the following:
299/// - `unique` (default): disallows duplicates;
300/// - `first`: takes first value and ignores subsequent ones;
301/// - `last`: takes last value and ignores previous ones.
302///
303/// ```rust
304/// # use syn::parse_quote;
305/// # use synthez::ParseAttrs;
306/// #
307/// #[derive(Default, ParseAttrs)]
308/// struct MyAttrs {
309///     /// Picks last appeared [`syn::Ident`] in attributes.
310///     #[parse(ident, dedup = last, alias = named)]
311///     name: Option<syn::Ident>,
312///
313///     /// Picks first value of `lit = <lit>` argument.
314///     #[parse(value, dedup = first)]
315///     lit: Option<syn::LitStr>,
316///
317///     /// Allows only one of `args`.
318///     #[parse(ident, dedup = unique, args(foo, bar, baz))]
319///     field: Option<syn::Ident>,
320/// }
321///
322/// # fn main() {
323/// let input: syn::DeriveInput = parse_quote! {
324///     #[my_attr(name, lit = "foo")]
325///     #[my_attr(named, lit = "bar")]
326///     #[my_attr(baz)]
327///     struct Dummy;
328/// };
329/// let my_attrs = MyAttrs::parse_attrs("my_attr", &input);
330///
331/// # assert!(my_attrs.is_ok());
332/// # let my_attrs = my_attrs.unwrap();
333/// assert_eq!(my_attrs.name, Some(parse_quote!(named)));
334/// assert_eq!(my_attrs.lit, Some(parse_quote!("foo")));
335/// assert_eq!(my_attrs.field, Some(parse_quote!(baz)));
336///
337/// let wrong: syn::DeriveInput = parse_quote! {
338///     #[my_attr(foo, bar)]
339///     #[my_attr(baz)]
340///     struct Dummy;
341/// };
342/// let my_attrs = MyAttrs::parse_attrs("my_attr", &wrong);
343///
344/// assert!(my_attrs.is_err());
345/// # }
346/// ```
347///
348/// ## `validate = <func>` (optional)
349///
350/// Allows to specify a function for additional validation of the parsed field
351/// value. The signature of the function should be the following:
352/// ```rust,ignore
353/// fn(&FieldType) -> syn::Result<()>
354/// ```
355///
356/// ```rust
357/// # use proc_macro2::Span;
358/// # use syn::parse_quote;
359/// # use synthez::ParseAttrs;
360/// #
361/// #[derive(Default, ParseAttrs)]
362/// struct MyAttrs {
363///     #[parse(value, validate = not_foo)]
364///     val: Option<syn::LitStr>,
365/// }
366///
367/// fn not_foo(lit: &Option<syn::LitStr>) -> syn::Result<()> {
368///     if lit.as_ref().map(syn::LitStr::value).as_deref() == Some("foo") {
369///         Err(syn::Error::new(Span::call_site(), "'foo' is not allowed"))
370///     } else {
371///         Ok(())
372///     }
373/// }
374///
375/// # fn main() {
376/// let wrong: syn::DeriveInput = parse_quote! {
377///     #[my_attr(val = "foo")]
378///     struct Dummy;
379/// };
380/// let my_attrs = MyAttrs::parse_attrs("my_attr", &wrong);
381///
382/// assert!(my_attrs.is_err());
383/// # }
384/// ```
385///
386/// ## `fallback = <func>` (optional)
387///
388/// Allows to specify a function producing a fallback value for the prased field
389/// value. The signature of the function should be the following:
390/// ```rust,ignore
391/// fn(&mut FieldType, ParsedInputType) -> syn::Result<()>
392/// ```
393///
394/// This fallback function is invoked every time the field is parsed, despite
395/// the kind of values it contains, so it's responsibility of the fallback
396/// function to determine whether applying fallback value is actually required.
397///
398/// Note, that this argument accepts expressions, so you may use
399/// [`field::if_empty()`] in a combination with a parse function to receive the
400/// required signature. In such case the parse function has a way more obvious
401/// signature:
402/// ```rust,ignore
403/// fn(ParsedInputType) -> syn::Result<ValueType>
404/// ```
405///
406/// ```rust
407/// # use syn::parse_quote;
408/// use synthez::{field, parse, ParseAttrs};
409///
410/// #[derive(Default, ParseAttrs)]
411/// struct MyAttrs {
412///     /// `fallback` will use doc comment as a value, if no `desc` argument is
413///     /// provided.
414///     #[parse(value, fallback = field::if_empty(parse::attr::doc))]
415///     desc: Option<syn::LitStr>,
416/// }
417///
418/// # fn main() {
419/// let from_attr: syn::DeriveInput = parse_quote! {
420///     /// bar
421///     #[my_attr(desc = "foo")]
422///     struct Dummy;
423/// };
424/// let my_attrs = MyAttrs::parse_attrs("my_attr", &from_attr);
425///
426/// # assert!(my_attrs.is_ok());
427/// # let my_attrs = my_attrs.unwrap();
428/// assert_eq!(my_attrs.desc, Some(parse_quote!("foo")));
429///
430/// let from_doc: syn::DeriveInput = parse_quote! {
431///     /// bar
432///     struct Dummy;
433/// };
434/// let my_attrs = MyAttrs::parse_attrs("my_attr", &from_doc);
435///
436/// # assert!(my_attrs.is_ok());
437/// # let my_attrs = my_attrs.unwrap();
438/// assert_eq!(my_attrs.desc, Some(parse_quote!("bar")));
439/// # }
440/// ```
441///
442/// [`field::Container`]: synthez_core::field::Container
443/// [`field::if_empty()`]: synthez_core::field::if_empty
444/// [`Parse`]: syn::parse::Parse
445/// [`Required`]: synthez_core::Required
446/// [`Spanned`]: syn::spanned::Spanned
447/// [`Spanning`]: synthez_core::Spanning
448/// [`synthez::ParseAttrs`]: synthez_core::ParseAttrs
449#[proc_macro_derive(ParseAttrs, attributes(parse))]
450pub fn derive_parse_attrs(input: TokenStream) -> TokenStream {
451    syn::parse(input)
452        .and_then(codegen::parse_attrs::derive)
453        .unwrap_or_else(syn::Error::into_compile_error)
454        .into()
455}
456
457/// Deriving of a [`quote::ToTokens`] implementation.
458///
459/// # Arguments
460///
461/// ## `append` (mandatory)
462///
463/// Specifies methods to form [`ToTokens`]' output with.
464///
465/// ```rust
466/// # use synthez::{proc_macro2::TokenStream, quote::quote, ToTokens};
467/// #
468/// #[derive(ToTokens)]
469/// #[to_tokens(append(foo_tokens, baz_tokens))]
470/// struct Dummy;
471///
472/// impl Dummy {
473///     fn foo_tokens(&self) -> TokenStream {
474///         quote! {
475///             impl Foo for String {}
476///         }
477///     }
478///
479///     fn baz_tokens(&self) -> TokenStream {
480///         quote! {
481///             impl Baz for String {}
482///         }
483///     }
484/// }
485///
486/// # fn main() {
487/// let dummy = Dummy;
488///
489/// assert_eq!(
490///     quote! { #dummy }.to_string(),
491///     quote! {
492///         impl Foo for String {}
493///         impl Baz for String {}
494///     }
495///     .to_string(),
496/// );
497/// # }
498/// ```
499///
500/// [`quote::ToTokens`]: synthez_core::quote::ToTokens
501/// [`ToTokens`]: synthez_core::quote::ToTokens
502#[proc_macro_derive(ToTokens, attributes(to_tokens))]
503pub fn derive_to_tokens(input: TokenStream) -> TokenStream {
504    syn::parse(input)
505        .and_then(|i| codegen::to_tokens::derive(&i))
506        .unwrap_or_else(syn::Error::into_compile_error)
507        .into()
508}