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}