Skip to main content

model_mapper_macros/
lib.rs

1mod input;
2mod model_mapper;
3mod type_path_ext;
4
5use proc_macro::TokenStream;
6use proc_macro_error2::proc_macro_error;
7
8/// Derive mapper functions to convert between types.
9///
10/// A `mapper` attribute is required at type-level and it's optional at field or variant level.
11///
12/// The following attributes are available:
13///
14/// #### Type level attributes
15///
16/// - `ty = PathType` _(**mandatory**)_: The other type to derive the conversion. Can be a string literal for complex
17///   types (e.g. `ty = "Type<T>"`)
18/// - `from` _(optional)_: Whether to derive `From` the other type for self
19///   - `custom` _(optional)_: Derive a custom function instead of the trait
20///   - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
21/// - `into` _(optional)_: Whether to derive `From` self for the other type
22///   - `custom` _(optional)_: Derive a custom function instead of the trait
23///   - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
24/// - `try_from` _(optional)_: Whether to derive `TryFrom` the other type for self
25///   - `custom` _(optional)_: Derive a custom function instead of the trait
26///   - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
27///   - `err = TargetError` _(optional)_: Explicit target error type for fallible conversions.
28///   - `accumulate` _(optional)_: Collect errors into `Vec<TargetError>` instead of short-circuiting on the first failure.
29///   - `accumulate = CustomAccumulator` _(optional)_: Collect errors into `CustomAccumulator` instead of `Vec`.
30/// - `try_into` _(optional)_: Whether to derive `TryFrom` self for the other type
31///   - `custom` _(optional)_: Derive a custom function instead of the trait
32///   - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
33///   - `err = TargetError` _(optional)_: Explicit target error type for fallible conversions.
34///   - `accumulate` _(optional)_: Collect errors into `Vec<TargetError>` instead of short-circuiting on the first failure.
35///   - `accumulate = CustomAccumulator` _(optional)_: Collect errors into `CustomAccumulator` instead of `Vec`.
36/// - `add` _(optional, multiple)_: Additional fields (for structs with named fields) or variants (for enums) the other
37///   type has and this one doesn't **&#xb9;**
38///   - `field = other_field` _(mandatory)_: The field or variant name
39///   - `ty = bool` _(optional)_: The field type, mandatory for `into` and `try_into` if no default value is provided
40///   - `default` _(optional)_: The field or variant will be populated using `Default::default()` (mandatory for enums,
41///     with or without value)
42///     - `value = true` _(optional)_: The field or variant will be populated with the given expression instead
43/// - `ignore_extra` _(optional)_: Whether to ignore all extra fields (for structs) or variants (for enums) of the other
44///   type **&#xb2;**
45///
46/// #### Variant level attributes
47///
48/// - `rename = OtherVariant` _(optional)_: To rename this variant on the other enum
49/// - `add` _(optional, multiple)_: Additional fields of the variant that the other type variant has and this one
50///   doesn't **&#xb9;**
51///   - `field = other_field` _(mandatory)_: The field name
52///   - `ty = bool` _(optional)_: The field type, mandatory for `into` and `try_into` if no default value is provided
53///   - `default` _(optional)_: The field or variant will be populated using `Default::default()`
54///     - `value = true` _(optional)_: The field or variant will be populated with the given expression instead
55/// - `skip` _(optional)_: Whether to skip this variant because the other enum doesn't have it
56///   - `default` _(mandatory)_: The field or variant will be populated using `Default::default()`
57///     - `value = get_default_value()` _(optional)_: The field or variant will be populated with the given expression
58///       instead
59/// - `ignore_extra` _(optional)_: Whether to ignore all extra fields of the other variant (only valid for _from_ and
60///   _try_from_) **&#xb2;**
61///
62/// #### Field level attributes
63///
64/// - `rename = other_name` _(optional)_: To rename this field on the other type
65/// - `other_ty = T` _(optional)_: If the field type corresponds to a generic parameter of the source type, this
66///   attribute allows specifying which generic parameter it maps to.
67/// - `skip` _(optional)_: Whether to skip this field because the other type doesn't have it
68///   - `default` _(optional)_: The field or variant will be populated using `Default::default()`
69///     - `value = get_default_value()` _(optional)_: The field or variant will be populated with the given expression
70///       instead
71/// - `err = ErrorVal` _(optional)_: Map conversion failures for this field to the specific error value `ErrorVal`. Used for error erasure
72///   where the original error is discarded (e.g. mapping to a unit variant error like `AppError::InvalidStatus`).
73/// - `err_with = MapFn` _(optional)_: Map conversion failures for this field using the helper function/callable `MapFn` (which can
74///   be a tuple variant constructor, a closure, or a function/method path). This preserves or maps the source error.
75///
76/// Additional hints on how to map fields:
77///
78/// - `opt` _(optional)_: The field is an `Option` and the inner value shall be mapped **&#xb3;**
79/// - `iter` _(optional)_: The field is an iterator and the inner value shall be mapped **&#xb3;**
80/// - `map` _(optional)_: The field is a hashmap-like iterator and the inner value shall be mapped **&#xb3;**
81/// - `boxed` _(optional)_: The field is a `Box` and the inner value shall be mapped **&#xb3;**
82/// - `box` _(optional)_: The other field is a `Box` while the current field is not **&#xb3;**
83/// - `unbox` _(optional)_: The current field is a `Box` while the other field is not **&#xb3;**
84/// - `with = mod::my_function` _(optional)_: If the field type doesn't implement `Into` or `TryInto` the other, this
85///   property allows you to customize the behavior by providing a conversion function
86/// - `from_with = mod::my_function` _(optional)_: The same as above but only for the `from` or `try_from` derives
87/// - `into_with = mod::my_function` _(optional)_: The same as above but only for the `into` or `try_into` derives
88///
89/// **&#xb9;** When providing additional fields without defaults, the `From` and `TryFrom` traits can't be derived and
90/// a custom function will be required instead. When deriving `into` or `try_into`, the `ty` must be provided as well.
91///
92/// **&#xb2;** When ignoring fields or variants it might be required that the enum or the struct implements `Default`
93/// in order to properly populate it.
94///
95/// **&#xb3;** Hints can be nested, for example: `opt(vec)`, `vec(opt(with = "my_custom_fn"))`
96///
97/// ## Example
98///
99/// ```rs
100/// #[derive(Mapper)]
101/// #[mapper(from, ty = Entity)]
102/// pub struct Model {
103///     id: i64,
104///     name: String,
105///     #[mapper(skip(default))]
106///     surname: Option<String>,
107/// }
108/// ```
109///
110/// Other advanced use cases are available on the [examples folder](https://github.com/lasantosr/model-mapper/tree/main/model-mapper/examples/).
111#[proc_macro_error]
112#[proc_macro_derive(Mapper, attributes(mapper))]
113pub fn model_mapper(input: TokenStream) -> TokenStream {
114    let input = syn::parse_macro_input!(input as syn::DeriveInput);
115    model_mapper::r#impl(input).into()
116}