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//!   - `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 **&#xb9;**
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 **&#xb2;**
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 **&#xb9;**
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_) **&#xb2;**
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 **&#xb3;**
165//!   - `iter` _(optional)_: The field is an iterator and the inner value shall be mapped **&#xb3;**
166//!   - `map` _(optional)_: The field is a hashmap-like iterator and the inner value shall be mapped **&#xb3;**
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//! **&#xb9;** 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//! **&#xb2;** 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//! **&#xb3;** 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}