1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
/*! [![github]](https://github.com/sbailleul/mapper) [![crates-io]](https://crates.io/crates/mapper) [![docs-rs]](https://docs.rs/mapper) [![mapper-ci]](https://github.com/sbailleul/mapper/actions/workflows/rust.yml)
[github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
[crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
[docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
[mapper-ci]: https://github.com/sbailleul/mapper/actions/workflows/rust.yml/badge.svg
This library provides a convenient derive macro for implementing [mapper_api::Mapper<T>] or [std::convert::Into] trait and generate mapping without boilerplate.
<br>
# Example
```ignore
# use std::io;
use mapper::Mapper;
fn map_account_id(account_id: &u16) -> String{
account_id.to_string()
}
#[derive(Mapper)]
#[to(Person)]
struct User{
#[to(Person, field=_name)]
pub name: String,
#[to(Person, with=map_account_id)]
pub account_id: u16,
pub age: u8
}
struct Person{
pub _name: String,
pub account_id: String,
pub age: u8
}
```
<br>
# Disclaimer
- Macro works only on structs
- Mapper doesn't handle nested properties
# Default behavior
Default behavior is to take each field of annotated struct and clone those fields in the destination struct initializer by implementing [mapper_api::Mapper<T>] trait :
```ignore
#[derive(Mapper)]
#[to(Person)]
struct User{
pub name: String
}
struct Person{
pub name: String
}
```
Generate 🔄 :
```ignore
impl Mapper<Person> for User{
fn to(&self)->Person{
Person{name: self.name.clone()}
}
}
```
# Mapping types
Two mapping types are available :
- Automatic, generate mapping for destinations specified in [to struct attributes](#to-struct-attribute),
all fields of the source are used for mapping if they are not explicitly excluded.
You can't use automatic mapping if additive mapping is already used for destination and strategy
- Additive, generate mapping for destinations specified in [to field attributes](#to-field-attribute), only annotated fields are mapped.
You can't use additive mapping if automatic mapping is already used for destination and strategy
# Mapping strategies
Two mapping strategies are available :
- mapper(default), map source to destination without consuming source, generate implementation of [mapper_api::Mapper<T>]
- into, map source to destination by consuming source of [std::convert::Into]
# To struct attribute
Generate automatic mapping for specified strategies.
- You can set multiple to attribute by struct
- Specify one or multiple destination types in this attribute : ```#[to(Animal, Vehicle)]```
- Specify one or multiple mapping strategies in this attribute : ```#[to(Animal, strategy=into, strategy=mapper)]```
# To field attribute
Complete automatic mapping configuration set on parent struct or provide additive mapping or exclude field from any mappings
- You can set multiple to attribute by field
- This attribute is forbidden if you use only DestinationType
## DestinationType
Type of the mapping destination. Mandatory argument unless field is unconditionally excluded.
### Generics
You can specify destination type with generics, these generics should be compatible with the fields of your src struct :
```ignore
#[derive(Mapper)]
#[to(Person::<String, u8>)]
struct User {
name: String,
age: u8
}
struct Person<T, U> {
name: T,
age: U
}
```
## Strategy
Trigger additive mapping for mapping destination and specified strategy e.g:
````ignore
#[derive(Mapper)]
struct User(#[to(Person, strategy=into)]u16, String);
struct Person(u16);
````
Generate 🔄 :
```ignore
impl Into<Person> for User{
fn into(self)->Person{
Person{0:self.0}
}
}
```
## Exclude
Optional parameter, specify if the field is excluded for mapping, there is 2 kind of exclusion.
- Unconditionally exclusion, exclude field of any kind of mapping e.g :
````ignore
#[derive(Mapper)]
#[to(Person)]
struct User(u16, #[to(exclude)]String);
struct Person(u16);
````
Generate 🔄 :
```ignore
impl Mapper<Person> for User{
fn into(self)->Person{
Person{0:self.0}
}
}
```
- Exclusion for specific destination (⚠️ not works for additive mapping), exclude field for specific destination mapping e.g :
````ignore
#[derive(Mapper)]
#[to(Person,Account, strategy=into)]
struct User(u16, #[to(Person,exclude)]String);
struct Account(u16, String);
struct Person(u16);
````
Generate 🔄 :
```ignore
impl Into<Person> for User{
fn into(self)->Person{
Person{0:self.0}
}
}
impl Into<Account> for User{
fn into(self)->Account{
Account{0:self.0, 1: self.1}
}
}
```
## Field
Optional parameter, target the destination type field e.g :
````ignore
#[derive(Mapper)]
#[to(Person)]
struct User{
#[to(Person, field=0)
name: String
};
struct Person( String);
````
Generate 🔄 :
```ignore
impl Mapper<Person> for User{
fn to(&self)->Person{
Person{0:self.name.clone()}
}
}
```
## With
Optional parameter, provide a function to transform the annotated field to the destination field.
You can specify strategy used by with function as following : ```with(<strategy>)``` if you use with without specifying strategy : ```with``` mapper strategy will be used by default
Signature of the function should be in regards of used strategy :
- with(mapper) | with :
```ignore
fn foo_mapping(val: &<src_field_type>)-><dst_field_type>
```
- with(into) :
```ignore
fn foo_mapping(val: <src_field_type>)-><dst_field_type>
```
### Generics
You can use generics in your function if the generic types constraint respect the source field type and destination field type :
```ignore
fn gen_func<T: Display>(val: &T)->String{
val.to_string()
}
#[derive(Mapper)]
#[to(Person)]
struct User {
#[to(Person, with=gen_func)]
age: u16,
}
struct Person {
age: String,
}
```
*/
pub use mapper_api::*;
pub use mapper_impl::*;