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//! - `try_into` _(optional)_: Whether to derive `TryFrom` self for the other type
122//! - `custom` _(optional)_: Derive a custom function instead of the trait
123//! - `custom = from_other` _(optional)_: Derive a custom function instead of the trait, with the given name
124//! - `add` _(optional, multiple)_: Additional fields (for structs with named fields) or variants (for enums) the
125//! other type has and this one doesn't **¹**
126//! - `field = other_field` _(mandatory)_: The field or variant name
127//! - `ty = bool` _(optional)_: The field type, mandatory for `into` and `try_into` if no default value is provided
128//! - `default` _(optional)_: The field or variant will be populated using `Default::default()` (mandatory for
129//! enums, with or without value)
130//! - `value = true` _(optional)_: The field or variant will be populated with the given expression instead
131//! - `ignore_extra` _(optional)_: Whether to ignore all extra fields (for structs) or variants (for enums) of the
132//! other type **²**
133//!
134//! - Variant level attributes:
135//!
136//! - `rename = OtherVariant` _(optional)_: To rename this variant on the other enum
137//! - `add` _(optional, multiple)_: Additional fields of the variant that the other type variant has and this one
138//! doesn't **¹**
139//! - `field = other_field` _(mandatory)_: The field name
140//! - `ty = bool` _(optional)_: The field type, mandatory for `into` and `try_into` if no default value is provided
141//! - `default` _(optional)_: The field or variant will be populated using `Default::default()`
142//! - `value = true` _(optional)_: The field or variant will be populated with the given expression instead
143//! - `skip` _(optional)_: Whether to skip this variant because the other enum doesn't have it
144//! - `default` _(mandatory)_: The field or variant will be populated using `Default::default()`
145//! - `value = get_default_value()` _(optional)_: The field or variant will be populated with the given expression
146//! instead
147//! - `ignore_extra` _(optional)_: Whether to ignore all extra fields of the other variant (only valid for _from_ and
148//! _try_from_) **²**
149//!
150//! - Field level attributes:
151//!
152//! - `rename = other_name` _(optional)_: To rename this field on the other type
153//! - `skip` _(optional)_: Whether to skip this field because the other type doesn't have it
154//! - `default` _(optional)_: The field or variant will be populated using `Default::default()`
155//! - `value = get_default_value()` _(optional)_: The field or variant will be populated with the given expression
156//! instead
157//! - `with = mod::my_function` _(optional)_: If the field type doesn't implement `Into` or `TryInto` the other, this
158//! property allows you to customize the behavior by providing a conversion function
159//! - `into_with = mod::my_function` _(optional)_: The same as above but only for the `into` or `try_into` derives
160//! - `from_with = mod::my_function` _(optional)_: The same as above but only for the `from` or `try_from` derives
161//!
162//! - Additional hints on how to map fields:
163//!
164//! - `opt` _(optional)_: The field is an `Option` and the inner value shall be mapped **³**
165//! - `iter` _(optional)_: The field is an iterator and the inner value shall be mapped **³**
166//! - `map` _(optional)_: The field is a hashmap-like iterator and the inner value shall be mapped **³**
167//! - `with = mod::my_function` _(optional)_: If the field type doesn't implement `Into` or `TryInto` the other, this
168//! property allows you to customize the behavior by providing a conversion function
169//! - `from_with = mod::my_function` _(optional)_: The same as above but only for the `from` or `try_from` derives
170//! - `from_with = mod::my_function` _(optional)_: The same as above but only for the `from` or `try_from` derives
171//!
172//! **¹** When providing additional fields without defaults, the `From` and `TryFrom` traits can't be derived and
173//! a custom function will be required instead. When deriving `into` or `try_into`, the `ty` must be provided as well.
174//!
175//! **²** When ignoring fields or variants it might be required that the enum or the struct implements `Default`
176//! in order to properly populate it.
177//!
178//! **³** Hints can be nested, for example: `opt(vec)`, `vec(opt(with = "my_custom_fn"))`
179//!
180//! ### Multiple derives
181//!
182//! When deriving conversions for a single type, attributes can be set directly:
183//!
184//! ```rust-ignore
185//! #[mapper(from, into, ty = OtherType, add(field = field_1, default), add(field = field_2, default))]
186//! struct MyStruct;
187//! ```
188//!
189//! But we can also derive conversions for multiple types by wrapping the properties on a `derive` attribute:
190//!
191//! ```rust-ignore
192//! #[mapper(derive(try_into, ty = OtherType, add(field = field_1, default)))]
193//! #[mapper(derive(from, ty = YetAnotherType))]
194//! struct MyStruct;
195//! ```
196//!
197//! If multiple conversions are involved, both variant and field level attributes can also be wrapped in a `when`
198//! attribute and must set the `ty` they refer to:
199//!
200//! ```rust-ignore
201//! #[mapper(when(ty = OtherType, with = ToString::to_string))]
202//! #[mapper(when(ty = YetAnotherType, skip(default)))]
203//! struct MyStruct;
204//! ```
205
206#![cfg_attr(not(feature = "std"), no_std)]
207
208// Re-export derive macro crate
209#[allow(unused_imports)]
210#[macro_use]
211extern crate model_mapper_macros;
212#[doc(hidden)]
213pub use model_mapper_macros::*;
214
215#[doc(hidden)]
216pub mod private {
217 pub trait RefMapper<T, R> {
218 fn map_value(&self, arg: T) -> R;
219 }
220 impl<F, T, R> RefMapper<T, R> for F
221 where
222 F: ?Sized + Fn(&T) -> R,
223 {
224 #[inline(always)]
225 fn map_value(&self, arg: T) -> R {
226 (self)(&arg)
227 }
228 }
229
230 pub trait ValueMapper<T, R> {
231 fn map_value(&self, arg: T) -> R;
232 }
233 impl<F, T, R> ValueMapper<T, R> for &F
234 where
235 F: ?Sized + Fn(T) -> R,
236 {
237 #[inline(always)]
238 fn map_value(&self, arg: T) -> R {
239 (*self)(arg)
240 }
241 }
242}