Skip to main content

model_mapper/
lib.rs

1//! # Model Mapper
2//!
3//! A powerful Rust macro to generate boilerplate-free declarations of `From`, `Into`, `TryFrom`, and `TryInto` traits
4//! for converting between structs and enums.
5//!
6//! It is designed to handle common patterns like detailed DTOs to internal entities, handling optional fields, nested
7//! collections, and even disparate generic types.
8//!
9//! ## Features
10//!
11//! - **Zero Boilerplate**: Automatically implements `From`, `Into`, `TryFrom`, and `TryInto`.
12//! - **Flexible Mapping**: Handle renamed fields, skipped fields, and additional fields.
13//! - **Custom Logic**: Inject custom conversion logic for specific fields using functions or expressions.
14//! - **Generics Support**: Seamless mapping between generic types with different parameters.
15//! - **Multiple Targets**: Map a single type to multiple other types with conditional configurations.
16//! - **Nested Mapping**: Built-in support for mapping inner values within Option, iterators, and maps.
17//! - **`no_std` compatible**: Works in `no_std` environments (with default features disabled).
18//!
19//! ## Quick Start
20//!
21//! The most common use case is mapping between domain entities and DTOs.
22//!
23//! ```rust
24//! # use model_mapper::Mapper;
25//! # struct Entity {
26//! #     id: i64,
27//! #     name: String,
28//! # }
29//! #[derive(Mapper)]
30//! #[mapper(from, ty = Entity)]
31//! pub struct Model {
32//!     id: i64,
33//!     name: String,
34//! }
35//! ```
36//!
37//! The macro expansion above would generate something like:
38//!
39//! ```rust
40//! # struct Entity { id: i64, name: String }
41//! # struct Model { id: i64, name: String }
42//! impl From<Entity> for Model {
43//!     fn from(Entity { id, name }: Entity) -> Self {
44//!         Self {
45//!             id: Into::into(id),
46//!             name: Into::into(name),
47//!         }
48//!     }
49//! }
50//! ```
51//!
52//! Because types doesn't always fit like a glove, you can provide additional fields on runtime, at the cost of not
53//! being able to use the `From` trait:
54//!
55//! ```rust
56//! # use model_mapper::Mapper;
57//! pub mod service {
58//!     pub struct UpdateUserInput {
59//!         pub user_id: i64,
60//!         pub name: Option<String>,
61//!         pub surname: Option<String>,
62//!     }
63//! }
64//!
65//! #[derive(Mapper)]
66//! #[mapper(
67//!     into(custom = "into_update_user"),
68//!     ty = service::UpdateUserInput,
69//!     add(field = user_id, ty = i64),
70//!     add(field = surname, default(value = None))
71//! )]
72//! pub struct UpdateProfileRequest {
73//!     pub name: String,
74//! }
75//! ```
76//!
77//! Would generate something like:
78//!
79//! ```rust
80//! # pub mod service {
81//! #    pub struct UpdateUserInput {
82//! #        pub user_id: i64,
83//! #        pub name: Option<String>,
84//! #        pub surname: Option<String>,
85//! #    }
86//! # }
87//! # struct UpdateProfileRequest { name: String }
88//! impl UpdateProfileRequest {
89//!     /// Builds a new [service::UpdateUserInput] from a [UpdateProfileRequest]
90//!     pub fn into_update_user(self, user_id: i64) -> service::UpdateUserInput {
91//!         let UpdateProfileRequest { name } = self;
92//!         service::UpdateUserInput {
93//!             user_id,
94//!             surname: None,
95//!             name: Into::into(name),
96//!         }
97//!     }
98//! }
99//! ```
100//!
101//! Other advanced use cases are available on the [examples folder](https://github.com/lasantosr/model-mapper/tree/main/model-mapper/examples/).
102//!
103//! ## Detailed Usage
104//!
105//! A `mapper` attribute is required at type-level and it's optional at field or variant level.
106//!
107//! The following attributes are available.
108//!
109//! - Type level attributes:
110//!
111//!   - `ty = PathType` _(**mandatory**)_: The other type to derive the conversion
112//!   - `from` _(optional)_: Whether to derive `From` the other type for self
113//!     - `custom` _(optional)_: Derive a custom function instead of the trait
114//!     - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
115//!   - `into` _(optional)_: Whether to derive `From` self for the other type
116//!     - `custom` _(optional)_: Derive a custom function instead of the trait
117//!     - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
118//!   - `try_from` _(optional)_: Whether to derive `TryFrom` the other type for self
119//!     - `custom` _(optional)_: Derive a custom function instead of the trait
120//!     - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
121//!     - `err = TargetError` _(optional)_: Explicit target error type for fallible conversions.
122//!     - `accumulate` _(optional)_: Collect errors into `Vec<TargetError>` instead of short-circuiting on the first failure.
123//!     - `accumulate = CustomAccumulator` _(optional)_: Collect errors into `CustomAccumulator` instead of `Vec`.
124//!   - `try_into` _(optional)_: Whether to derive `TryFrom` self for the other type
125//!     - `custom` _(optional)_: Derive a custom function instead of the trait
126//!     - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
127//!     - `err = TargetError` _(optional)_: Explicit target error type for fallible conversions.
128//!     - `accumulate` _(optional)_: Collect errors into `Vec<TargetError>` instead of short-circuiting on the first failure.
129//!     - `accumulate = CustomAccumulator` _(optional)_: Collect errors into `CustomAccumulator` instead of `Vec`.
130//!   - `add` _(optional, multiple)_: Additional fields (for structs with named fields) or variants (for enums) the
131//!     other type has and this one doesn't **&#xb9;**
132//!     - `field = other_field` _(mandatory)_: The field or variant name
133//!     - `ty = bool` _(optional)_: The field type, mandatory for `into` and `try_into` if no default value is provided
134//!     - `default` _(optional)_: The field or variant will be populated using `Default::default()` (mandatory for
135//!       enums, with or without value)
136//!       - `value = true` _(optional)_: The field or variant will be populated with the given expression instead
137//!   - `ignore_extra` _(optional)_: Whether to ignore all extra fields (for structs) or variants (for enums) of the
138//!     other type **&#xb2;**
139//!
140//! - Variant level attributes:
141//!
142//!   - `rename = OtherVariant` _(optional)_: To rename this variant on the other enum
143//!   - `add` _(optional, multiple)_: Additional fields of the variant that the other type variant has and this one
144//!     doesn't **&#xb9;**
145//!     - `field = other_field` _(mandatory)_: The field name
146//!     - `ty = bool` _(optional)_: The field type, mandatory for `into` and `try_into` if no default value is provided
147//!     - `default` _(optional)_: The field or variant will be populated using `Default::default()`
148//!       - `value = true` _(optional)_: The field or variant will be populated with the given expression instead
149//!   - `skip` _(optional)_: Whether to skip this variant because the other enum doesn't have it
150//!     - `default` _(mandatory)_: The field or variant will be populated using `Default::default()`
151//!       - `value = get_default_value()` _(optional)_: The field or variant will be populated with the given expression
152//!         instead
153//!   - `ignore_extra` _(optional)_: Whether to ignore all extra fields of the other variant (only valid for _from_ and
154//!     _try_from_) **&#xb2;**
155//!
156//! - Field level attributes:
157//!
158//!   - `rename = other_name` _(optional)_: To rename this field on the other type
159//!   - `skip` _(optional)_: Whether to skip this field because the other type doesn't have it
160//!     - `default` _(optional)_: The field or variant will be populated using `Default::default()`
161//!       - `value = get_default_value()` _(optional)_: The field or variant will be populated with the given expression
162//!         instead
163//!   - `err = ErrorVal` _(optional)_: Map conversion failures for this field to the specific error value `ErrorVal`. Used for error erasure where the original error is discarded (e.g. mapping to a unit variant error like `AppError::InvalidStatus`).
164//!   - `err_with = MapFn` _(optional)_: Map conversion failures for this field using the helper function/callable `MapFn` (which can be a tuple variant constructor, a closure, or a function/method path). This preserves or maps the source error.
165//!
166//! - Additional hints on how to map fields:
167//!
168//!   - `opt` _(optional)_: The field is an `Option` and the inner value shall be mapped **&#xb3;**
169//!   - `iter` _(optional)_: The field is an iterator and the inner value shall be mapped **&#xb3;**
170//!   - `map` _(optional)_: The field is a hashmap-like iterator and the inner value shall be mapped **&#xb3;**
171//!   - `boxed` _(optional)_: The field is a `Box` and the inner value shall be mapped **&#xb3;**
172//!   - `box` _(optional)_: The other field is a `Box` while the current field is not **&#xb3;**
173//!   - `unbox` _(optional)_: The current field is a `Box` while the other field is not **&#xb3;**
174//!   - `with = mod::my_function` _(optional)_: If the field type doesn't implement `Into` or `TryInto` the other, this
175//!     property allows you to customize the behavior by providing a conversion function
176//!   - `into_with = mod::my_function` _(optional)_: The same as above but only for the `into` or `try_into` derives
177//!   - `from_with = mod::my_function` _(optional)_: The same as above but only for the `from` or `try_from` derives
178//!
179//! **&#xb9;** When providing additional fields without defaults, the `From` and `TryFrom` traits can't be derived and
180//! a custom function will be required instead. When deriving `into` or `try_into`, the `ty` must be provided as well.
181//!
182//! **&#xb2;** When ignoring fields or variants it might be required that the enum or the struct implements `Default`
183//! in order to properly populate it.
184//!
185//! **&#xb3;** Hints can be nested, for example: `opt(vec)`, `vec(opt(with = "my_custom_fn"))`
186//!
187//! ### Multiple derives
188//!
189//! When deriving conversions for a single type, attributes can be set directly:
190//!
191//! ```rust-ignore
192//! #[mapper(from, into, ty = OtherType, add(field = field_1, default), add(field = field_2, default))]
193//! struct MyStruct;
194//! ```
195//!
196//! But we can also derive conversions for multiple types by wrapping the properties on a `derive` attribute:
197//!
198//! ```rust-ignore
199//! #[mapper(derive(try_into, ty = OtherType, add(field = field_1, default)))]
200//! #[mapper(derive(from, ty = YetAnotherType))]
201//! struct MyStruct;
202//! ```
203//!
204//! If multiple conversions are involved, both variant and field level attributes can also be wrapped in a `when`
205//! attribute and must set the `ty` they refer to:
206//!
207//! ```rust-ignore
208//! #[mapper(when(ty = OtherType, with = ToString::to_string))]
209//! #[mapper(when(ty = YetAnotherType, skip(default)))]
210//! struct MyStruct;
211//! ```
212
213#![cfg_attr(not(feature = "std"), no_std)]
214
215// Re-export derive macro crate
216#[allow(unused_imports)]
217#[macro_use]
218extern crate model_mapper_macros;
219#[doc(hidden)]
220pub use model_mapper_macros::*;
221
222#[doc(hidden)]
223pub mod private {
224    pub trait RefMapper<T, R> {
225        fn map_value(&self, arg: T) -> R;
226    }
227    impl<F, T, R> RefMapper<T, R> for F
228    where
229        F: ?Sized + Fn(&T) -> R,
230    {
231        #[inline(always)]
232        fn map_value(&self, arg: T) -> R {
233            (self)(&arg)
234        }
235    }
236
237    pub trait ValueMapper<T, R> {
238        fn map_value(&self, arg: T) -> R;
239    }
240    impl<F, T, R> ValueMapper<T, R> for &F
241    where
242        F: ?Sized + Fn(T) -> R,
243    {
244        #[inline(always)]
245        fn map_value(&self, arg: T) -> R {
246            (*self)(arg)
247        }
248    }
249}