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