Object to Object mapper for Rust

github.com crates.io

Content

Quick pitch

rust impl From<Person> for PersonDto { fn from(value: Person) -> PersonDto { PersonDto { id: value.id, name: value.name, age: value.age, } } }

Writing code like above is not the most exciting or rewarding part of working with Rust. If you're Ok with letting procedural macro write it for you, welcome to the rest of this page.

Brief description

o2o procedural macro is able to generate implementation of 6 kinds of traits (currently for structs only):

``` rust // #[from_owned()] impl std::convert::From for B { ... }

// #[from_ref()] impl std::convert::From<&A> for B { ... }

// #[owned_into()] impl std::convert::Into for B { ... }

// #[ref_into()] impl std::convert::Into for &B { ... }

// #[ownedintoexisting()] impl o2o::traits::IntoExisting for B { ... }

// #[refintoexisting()] impl o2o::traits::IntoExisting for &B { ... } ```

It also has shortcuts to configure multiple trait implementations with fewer lines of code:

| | #[map()] | #[from()] | #[into()] | #[mapowned()] | #[mapref()] | #[intoexisting()] | | ---------------------------- | -------- | ---------- | --------- | ---------------| ------------ | -------------------| | #[fromowned()] | ✔️ | ✔️ | ❌ | ✔️ | ❌ | ❌ | | #[fromref()] | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ | | #[ownedinto()] | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ | | #[refinto()] | ✔️ | ❌ | ✔️ | ❌ | ✔️ | ❌ | | #[ownedintoexisting()] | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | | #[refinto_existing()] | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ |

With that, let's look at some examples.

Examples

Simplest Case

``` rust use o2o::o2o;

struct Person { id: u32, name: String, age: u8 }

[derive(o2o)]

[map_owned(Person)]

struct PersonDto { id: u32, name: String, age: u8 } ``` From here on, generated code is produced by rust-analyzer: Expand macro recursively command

View generated code

rust impl std::convert::From<Person> for PersonDto { fn from(value: Person) -> PersonDto { PersonDto { id: value.id, name: value.name, age: value.age, } } } impl std::convert::Into<Person> for PersonDto { fn into(self) -> Person { Person { id: self.id, name: self.name, age: self.age, } } }

With the above code you should be able to do this:

rust let person = Person { id: 123, name: "John".into(), age: 42 }; let dto: PersonDto = person.into(); // and this: let dto = PersonDto { id: 123, name: "John".into(), age: 42 }; let person: Person = dto.into();

Different field name

``` rust struct Entity { someint: i32, anotherint: i16, }

[derive(o2o)]

[from_ref(Entity)]

[refintoexisting(Entity)]

struct EntityDto { someint: i32, #[map(anotherint)] different_int: i16, } ```

View generated code

rust impl std::convert::From<&Entity> for EntityDto { fn from(value: &Entity) -> EntityDto { EntityDto { some_int: value.some_int, different_int: value.another_int, } } } impl o2o::traits::IntoExisting<Entity> for &EntityDto { fn into_existing(self, other: &mut Entity) { other.some_int = self.some_int; other.another_int = self.different_int; } }

Different field type

``` rust struct Entity { some_int: i32, val: i16, str: String }

[derive(o2o)]

[map(Entity)]

struct EntityDto { someint: i32, #[from(~.tostring())] // Tilde allows to append code at the end of the right side of field initialization for From impls #[into(~.parse::().unwrap())] // here it's the same but for Into impls val: String, // Here Into and From are symmetric, so it has to be only specified once. // Note that .clone() is only needed for borrowing impls, so we use #[mapref()] #[mapref(~.clone())] str: String } ```

View generated code

rust impl std::convert::From<Entity> for EntityDto { fn from(value: Entity) -> EntityDto { EntityDto { some_int: value.some_int, val: value.val.to_string(), str: value.str, // no .clone() } } } impl std::convert::From<&Entity> for EntityDto { fn from(value: &Entity) -> EntityDto { EntityDto { some_int: value.some_int, val: value.val.to_string(), str: value.str.clone(), } } } impl std::convert::Into<Entity> for EntityDto { fn into(self) -> Entity { Entity { some_int: self.some_int, val: self.val.parse::<i16>().unwrap(), str: self.str, // no .clone() } } } impl std::convert::Into<Entity> for &EntityDto { fn into(self) -> Entity { Entity { some_int: self.some_int, val: self.val.parse::<i16>().unwrap(), str: self.str.clone(), } } }

Nested structs

``` rust struct Entity { someint: i32, child: Child, } struct Child { childint: i32, }

[derive(o2o)]

[from_owned(Entity)]

struct EntityDto { some_int: i32, #[map(~.into())] child: ChildDto }

[derive(o2o)]

[from_owned(Child)]

struct ChildDto { child_int: i32, } ```

View generated code

``` rust impl std::convert::From for EntityDto { fn from(value: Entity) -> EntityDto { EntityDto { someint: value.someint, child: value.child.into(), } } }

impl std::convert::From for ChildDto { fn from(value: Child) -> ChildDto { ChildDto { childint: value.childint, } } } ```

Nested collection

``` rust struct Entity { someint: i32, children: Vec, } struct Child { childint: i32, }

[derive(o2o)]

[map_owned(Entity)]

struct EntityDto { someint: i32, // Here field name as well as type are different, so we pass in field name and tilde inline expression. // Also, it doesn't hurt to use member level instruction #[map()], // which is broader than struct level instruction #[mapowned] #[map(children, ~.iter().map(|p|p.into()).collect())] children_vec: Vec }

[derive(o2o)]

[map_ref(Child)]

struct ChildDto { child_int: i32, } ```

View generated code

rust impl std::convert::From<Entity> for EntityDto { fn from(value: Entity) -> EntityDto { EntityDto { some_int: value.some_int, children_vec: value.children.iter().map(|p| p.into()).collect(), } } } impl std::convert::Into<Entity> for EntityDto { fn into(self) -> Entity { Entity { some_int: self.some_int, children: self.children_vec.iter().map(|p| p.into()).collect(), } } } impl std::convert::From<&Child> for ChildDto { fn from(value: &Child) -> ChildDto { ChildDto { child_int: value.child_int, } } } impl std::convert::Into<Child> for &ChildDto { fn into(self) -> Child { Child { child_int: self.child_int, } } }

Assymetric fields (skipping and providing default values)

o2o is able to handle scenarios when either of the structs has a field that the other struct doesn't have.

For the scenario where you put o2o instructions on a struct that contains extra field: ``` rust struct Person { id: i32, full_name: String, age: i8, }

[derive(o2o)]

[map_owned(Person)]

struct PersonDto { id: i32, fullname: String, age: i8, // (|| None) below provides default value when creating PersonDto from Person // It could have been omited if we only needed to create Person from PersonDto #[ghost(|| None)] zodiacsign: Option } enum ZodiacSign {} ```

View generated code

rust impl std::convert::From<Person> for PersonDto { fn from(value: Person) -> PersonDto { PersonDto { id: value.id, full_name: value.full_name, age: value.age, zodiac_sign: (|| None)(), } } } impl std::convert::Into<Person> for PersonDto { fn into(self) -> Person { Person { id: self.id, full_name: self.full_name, age: self.age, } } }

In a reverse case, you need to use a struct level #[ghost()] instruction: ``` rust

[derive(o2o)]

[map_owned(PersonDto)]

[ghost(zodiac_sign: || { None })] // #[ghost()] struct level instruction accepts only braced closures.

struct Person { id: i32, full_name: String, age: i8, }

struct PersonDto { id: i32, fullname: String, age: i8, zodiacsign: Option } enum ZodiacSign {} ```

View generated code

rust impl std::convert::From<PersonDto> for Person { fn from(value: PersonDto) -> Person { Person { id: value.id, full_name: value.full_name, age: value.age, } } } impl std::convert::Into<PersonDto> for Person { fn into(self) -> PersonDto { PersonDto { id: self.id, full_name: self.full_name, age: self.age, zodiac_sign: (|| None)(), } } }

Expressions

Expressions for struct level instructions

```rust

[ghost(field: { None })]

// field: None,

[ghost(field: || { None })]

// field: (|| None)(),

[ghost(field: { @.get_value() })]

// field: self.get_value(),

[ghost(field: |x| { x.get_value() })]

// field: (|x: &Type| x.get_value())(&self), ```

Expressions for member level instructions

```rust

[map({ None })]

// field: None,

[map(~.clone())]

// field: value.field.clone(),

[map(@.get_value())]

// field: value.get_value(),

[map(|x| x.get_value())]

// field: (|x: &Type| x.get_value())(&value), ```

More examples

Slightly complex example

``` rust struct Employee { id: i32, firstname: String, lastname: String, subordinateof: Box, subordinates: Vec> } impl Employee { fn getfullname(&self) -> String { format!("{} {}", self.firstname, self.last_name) } }

[derive(o2o)]

[map(Employee)]

[ghost(

// o2o supports closures with one input parameter.
// This parameter represents instance on the other side of the conversion.
first_name: |x| {x.get_first_name()},
last_name: |x| {x.get_last_name()}

)] struct EmployeeDto { #[map(id)] employeeid: i32, // '@.' is another flavor of 'inline expression'. // @ also represents instance on the other side of the conversion. #[ghost(@.getfullname())] fullname: String,

#[from(|x| Box::new(x.subordinate_of.as_ref().into()))]
#[into(subordinate_of, |x| Box::new(x.reports_to.as_ref().into()))]
reports_to: Box<EmployeeDto>,

#[map(~.iter().map(|p|Box::new(p.as_ref().into())).collect())]
subordinates: Vec<Box<EmployeeDto>>

} impl EmployeeDto { fn getfirstname(&self) -> String { self.fullname.splitwhitespace().collect::>()[0].into() } fn getlastname(&self) -> String { self.fullname.splitwhitespace().collect::>()[1].into() } } ```

View generated code

rust impl std::convert::From<Employee> for EmployeeDto { fn from(value: Employee) -> EmployeeDto { EmployeeDto { employee_id: value.id, full_name: value.get_full_name(), reports_to: (|x: &Employee| Box::new(x.subordinate_of.as_ref().into()))(&value), subordinates: value.subordinates.iter().map(|p| Box::new(p.as_ref().into())).collect(), } } } impl std::convert::From<&Employee> for EmployeeDto { fn from(value: &Employee) -> EmployeeDto { EmployeeDto { employee_id: value.id, full_name: value.get_full_name(), reports_to: (|x: &Employee| Box::new(x.subordinate_of.as_ref().into()))(value), subordinates: value.subordinates.iter().map(|p| Box::new(p.as_ref().into())).collect(), } } } impl std::convert::Into<Employee> for EmployeeDto { fn into(self) -> Employee { Employee { id: self.employee_id, subordinate_of: (|x: &EmployeeDto| Box::new(x.reports_to.as_ref().into()))(&self), subordinates: self.subordinates.iter().map(|p| Box::new(p.as_ref().into())).collect(), first_name: (|x: &EmployeeDto| x.get_first_name())(&self), last_name: self.get_last_name(), } } } impl std::convert::Into<Employee> for &EmployeeDto { fn into(self) -> Employee { Employee { id: self.employee_id, subordinate_of: (|x: &EmployeeDto| Box::new(x.reports_to.as_ref().into()))(self), subordinates: self.subordinates.iter().map(|p| Box::new(p.as_ref().into())).collect(), first_name: (|x: &EmployeeDto| x.get_first_name())(self), last_name: self.get_last_name(), } } }

Flatened children

When the instructions are put on the side that contains flatened properties, conversion From<T> and IntoExisting<T> only requires usage of a member level #[child(...)] instruction, which accepts a path to the unflatened field (without the field name itself). ``` rust struct Car { numberofdoors: i8, vehicle: Vehicle } struct Vehicle { numberofseats: i16, machine: Machine, } struct Machine { brand: String, year: i16 }

[derive(o2o)]

[from_owned(Car)]

[refintoexisting(Car)]

struct CarDto { numberofdoors: i8,

#[child(vehicle)]
number_of_seats: i16,

#[child(vehicle.machine)]
#[map_ref(~.clone())]
brand: String,

#[child(vehicle.machine)]
year: i16

} ```

View generated code

rust impl std::convert::From<Car> for CarDto { fn from(value: Car) -> CarDto { CarDto { number_of_doors: value.number_of_doors, number_of_seats: value.vehicle.number_of_seats, brand: value.vehicle.machine.brand, year: value.vehicle.machine.year, } } } impl o2o::traits::IntoExisting<Car> for &CarDto { fn into_existing(self, other: &mut Car) { other.number_of_doors = self.number_of_doors; other.vehicle.number_of_seats = self.number_of_seats; other.vehicle.machine.brand = self.brand.clone(); other.vehicle.machine.year = self.year; } }

When you need an Into<T> conversion, o2o also expects you to provide types for flatened properties via struct level #[children(...)] instruction:

``` rust

[derive(o2o)]

[owned_into(Car)]

[children(vehicle: Vehicle, vehicle.machine: Machine)]

struct CarDto { numberofdoors: i8,

#[child(vehicle)]
number_of_seats: i16,

#[child(vehicle.machine)]
brand: String,

#[child(vehicle.machine)]
year: i16

} ```

View generated code

rust impl std::convert::Into<Car> for CarDto { fn into(self) -> Car { Car { number_of_doors: self.number_of_doors, vehicle: Vehicle { number_of_seats: self.number_of_seats, machine: Machine { brand: self.brand, year: self.year, }, }, } } }

The reverse case, where you have to put o2o insturctions on the side that has field that are being flatened, is slightly tricky:

``` rust

[derive(o2o)]

[owned_into(CarDto)]

struct Car { numberofdoors: i8, #[parent] vehicle: Vehicle }

[derive(o2o)]

[ownedintoexisting(CarDto)]

struct Vehicle { numberofseats: i16, #[parent] machine: Machine, }

[derive(o2o)]

[ownedintoexisting(CarDto)]

struct Machine { brand: String, year: i16 }

// CarDto has to implement Default trait in this case.

[derive(Default)]

struct CarDto { numberofdoors: i8, numberofseats: i16, brand: String, year: i16 } ```

View generated code

rust impl std::convert::Into<CarDto> for Car { fn into(self) -> CarDto { let mut obj = CarDto { number_of_doors: self.number_of_doors, ..Default::default() }; self.vehicle.into_existing(&mut obj); obj } } impl o2o::traits::IntoExisting<CarDto> for Vehicle { fn into_existing(self, other: &mut CarDto) { other.number_of_seats = self.number_of_seats; self.machine.into_existing(other); } } impl o2o::traits::IntoExisting<CarDto> for Machine { fn into_existing(self, other: &mut CarDto) { other.brand = self.brand; other.year = self.year; } }

Tuple structs

``` rust struct TupleEntity(i32, String);

[derive(o2o)]

[map_ref(TupleEntity)]

struct TupleEntityDto(i32, #[map_ref(~.clone())] String); ```

View generated code

rust impl std::convert::From<&TupleEntity> for TupleEntityDto { fn from(value: &TupleEntity) -> TupleEntityDto { TupleEntityDto(value.0, value.1.clone()) } } impl std::convert::Into<TupleEntity> for &TupleEntityDto { fn into(self) -> TupleEntity { TupleEntity(self.0, self.1.clone()) } }

As long as Rust allows following syntax, easy conversion between tuple and named structs can be done if placing o2o instructions on named side:

``` rust struct TupleEntity(i32, String);

[derive(o2o)]

[map_ref(TupleEntity)]

struct EntityDto { #[mapref(0)] someint: i32, #[mapref(1, ~.clone())] somestr: String } ```

View generated code

rust impl std::convert::From<&TupleEntity> for EntityDto { fn from(value: &TupleEntity) -> EntityDto { EntityDto { some_int: value.0, some_str: value.1.clone(), } } } impl std::convert::Into<TupleEntity> for &EntityDto { fn into(self) -> TupleEntity { TupleEntity { 0: self.some_int, 1: self.some_str.clone(), } } }

Struct kind hints

By default, o2o will suppose that the struct on the other side is the same kind of struct that the original one is. In order to convert between named and tuple structs when you need to place instructions on a tuple side, you`ll need to use Struct Kind Hint:

``` rust

[derive(o2o)]

[map_owned(EntityDto as {})]

struct TupleEntity(#[map(someint)] i32, #[map(somestr)] String);

struct EntityDto{ someint: i32, somestr: String } ```

View generated code

rust impl std::convert::From<EntityDto> for TupleEntity { fn from(value: EntityDto) -> TupleEntity { TupleEntity(value.some_int, value.some_str) } } impl std::convert::Into<EntityDto> for TupleEntity { fn into(self) -> EntityDto { EntityDto { some_int: self.0, some_str: self.1, } } }

Generics

``` rust struct Entity { some_int: i32, something: T, }

[derive(o2o)]

[map_owned(Entity::)]

struct EntityDto { some_int: i32, something: f32 } ```

View generated code

rust impl std::convert::From<Entity<f32>> for EntityDto { fn from(value: Entity<f32>) -> EntityDto { EntityDto { some_int: value.some_int, something: value.something, } } } impl std::convert::Into<Entity<f32>> for EntityDto { fn into(self) -> Entity<f32> { Entity::<f32> { some_int: self.some_int, something: self.something, } } }

Where clauses

``` rust struct Child { child_int: i32, something: T, }

[derive(o2o)]

[map_owned(Child::)]

[where_clause(T: Clone)]

struct ChildDto { child_int: i32, #[map(something, ~.clone())] stuff: T, } ```

View generated code

rust impl<T> std::convert::From<Child<T>> for ChildDto<T> where T: Clone, { fn from(value: Child<T>) -> ChildDto<T> { ChildDto { child_int: value.child_int, stuff: value.something.clone(), } } } impl<T> std::convert::Into<Child<T>> for ChildDto<T> where T: Clone, { fn into(self) -> Child<T> { Child::<T> { child_int: self.child_int, something: self.stuff.clone(), } } }

Mapping to multiple structs

``` rust struct Person { full_name: String, age: i32, country: String, }

struct PersonModel { fullname: String, age: i32, placeof_birth: String, }

[derive(o2o)]

[ref_into(Person)]

[ref_into(PersonModel)]

struct PersonDto { // 'Default' member level instruction applies to all types #[into(fullname, ~.clone())] name: String, age: i32, // 'Dedicated' member level instruction applies to a specific type only #[into(Person| country, ~.clone())] #[into(PersonModel| ~.clone())] placeof_birth: String, } ```

View generated code

rust impl std::convert::Into<Person> for &PersonDto { fn into(self) -> Person { Person { full_name: self.name.clone(), age: self.age, country: self.place_of_birth.clone(), } } } impl std::convert::Into<PersonModel> for &PersonDto { fn into(self) -> PersonModel { PersonModel { full_name: self.name.clone(), age: self.age, place_of_birth: self.place_of_birth.clone(), } } }

Avoiding proc macro attribute name collisions (alternative instruction syntax)

o2o proc macro declares a lot of attributes, some of which have pretty broad meaning (e.g. from, into, map, child, parent etc.), so if you have to use it with some other proc macro, there is a chance that these attributes can collide and it would not be clear to what proc macro they should apply. For this scenario, o2o supports two alternative syntaxes (syntacies?):

Below, all three variants of o2o proc macro application will produce the same generated code: ``` rust struct Entity { some_int: i32, val: i16, str: String } // =====================================================================

[derive(o2o)]

[map(Entity)]

struct EntityDto { someint: i32, #[from(~.tostring())] #[into(~.parse::().unwrap())] val: String, #[map_ref(~.clone())] str: String } // =====================================================================

[derive(o2o)]

[o2o(map(Entity))]

struct EntityDto { someint: i32, #[o2o(from(~.tostring()))] #[o2o(into(~.parse::().unwrap()))] val: String, #[o2o(map_ref(~.clone()))] str: String } // =====================================================================

[derive(o2o)]

[o2o(map(Entity))]

struct EntityDto { someint: i32, #[o2o( from(~.tostring()), into(~.parse::().unwrap()), )] val: String, #[o2o(map_ref(~.clone()))] str: String } // ===================================================================== ```

This syntax applies to all supported struct and member level instructions.

Additional o2o instruction available via #[o2o(...)] syntax

Primitive type conversions

``` rust struct Entity { someint: i32, somefloat: f32 }

[derive(o2o)]

[o2o(map_ref(Entity))]

struct EntityDto { #[o2o(astype(i32))] someint: i16, #[o2o(astype(somefloat, f32))] another_int: i16 } ```

View generated code

rust impl std::convert::From<&Entity> for EntityDto { fn from(value: &Entity) -> EntityDto { EntityDto { some_int: value.some_int as i16, another_int: value.some_float as i16, } } } impl std::convert::Into<Entity> for &EntityDto { fn into(self) -> Entity { Entity { some_int: self.some_int as i32, some_float: self.another_int as f32, } } }

This will work with all types that support 'as' conversion.

Repeat instructions

```rust struct Car { numberofdoors: i8, vehicle: Vehicle } struct Vehicle { numberofseats: i16, canfly: bool, needsdriver: bool, horsepower: i32, top_speed: f32, machine: Machine, } struct Machine { id: i32, brand: String, year: i16, weight: f32, length: f32, width: f32, height: f32, }

[derive(o2o)]

[map_ref(Car)]

[children(vehicle: Vehicle, vehicle.machine: Machine)]

[ghost(vehicle.machine@id: || { 321 })]

struct CarDto { numberofdoors: i8,

// #[o2o(repeat)] will repeat all instructions for this member to the following members, 
// until there is a #[o2o(stop_repeat)] or the members run out.
#[o2o(repeat)] #[child(vehicle)]
number_of_seats: i16,
can_fly: bool,
needs_driver: bool,
horsepower: i32,
top_speed: f32,
#[o2o(stop_repeat)]

// You can also specify what specific types of instructions to repeat
// (supported values are 'map', 'child', 'parent', 'ghost')
#[o2o(repeat(child))] #[child(vehicle.machine)]
#[map(~.clone())]
brand: String,
year: i16,
weight: f32,
length: f32,
width: f32,
height: f32,
#[o2o(stop_repeat)]

#[o2o(repeat)] #[ghost(|| {123})]
useless_param: i32,
useless_param_2: i32,
useless_param_3: i32,

} ```

View generated code

rust impl std::convert::From<&Car> for CarDto { fn from(value: &Car) -> CarDto { CarDto { number_of_doors: value.number_of_doors, number_of_seats: value.vehicle.number_of_seats, can_fly: value.vehicle.can_fly, needs_driver: value.vehicle.needs_driver, horsepower: value.vehicle.horsepower, top_speed: value.vehicle.top_speed, brand: value.vehicle.machine.brand.clone(), year: value.vehicle.machine.year, weight: value.vehicle.machine.weight, length: value.vehicle.machine.length, width: value.vehicle.machine.width, height: value.vehicle.machine.height, useless_param: (|| 123)(), useless_param_2: (|| 123)(), useless_param_3: (|| 123)(), } } } impl std::convert::Into<Car> for &CarDto { fn into(self) -> Car { Car { number_of_doors: self.number_of_doors, vehicle: Vehicle { number_of_seats: self.number_of_seats, can_fly: self.can_fly, needs_driver: self.needs_driver, horsepower: self.horsepower, top_speed: self.top_speed, machine: Machine { brand: self.brand.clone(), year: self.year, weight: self.weight, length: self.length, width: self.width, height: self.height, id: (|| 321)(), }, }, } } }

Wrapper structs and #[o2o(wrapper)] instruction

```rust

[derive(o2o)]

[map_owned(Vec)]

struct PayloadWrapper { #[o2o(wrapper)] payload: Vec, } ```

View generated code

rust impl std::convert::From<Vec<u8>> for PayloadWrapper { fn from(value: Vec<u8>) -> PayloadWrapper { PayloadWrapper { payload: value } } } impl std::convert::Into<Vec<u8>> for PayloadWrapper { fn into(self) -> Vec<u8> { self.payload } }

If you need 'ref' implementation, you may need to provide an inline expression:

```rust

[derive(o2o)]

[map(String)]

struct StringWrapper { #[o2o(wrapper(~.clone()))] str: String } ```

View generated code

rust impl std::convert::From<String> for StringWrapper { fn from(value: String) -> StringWrapper { StringWrapper { str: value } } } impl std::convert::From<&String> for StringWrapper { fn from(value: &String) -> StringWrapper { StringWrapper { str: value.clone() } } } impl std::convert::Into<String> for StringWrapper { fn into(self) -> String { self.str } } impl std::convert::Into<String> for &StringWrapper { fn into(self) -> String { self.str.clone() } }

Wrapped structs and #[o2o(wrapped)] instruction

```rust

[derive(o2o)]

[map_owned(StuffWrapper as {})]

[o2o(wrapped(payload))]

struct Stuff(i32);

struct StuffWrapper { payload: Stuff, } ```

View generated code

rust impl std::convert::From<StuffWrapper> for Stuff { fn from(value: StuffWrapper) -> Stuff { value.payload } } impl std::convert::Into<StuffWrapper> for Stuff { fn into(self) -> StuffWrapper { StuffWrapper { payload: self } } }

'Ref' implementation

```rust

[derive(Clone, o2o)]

[map(StuffWrapper as ())]

[o2o(wrapped(0, ~.clone()))]

struct Stuff { thing: i32 }

struct StuffWrapper(Stuff); ```

View generated code

rust impl std::convert::From<StuffWrapper> for Stuff { fn from(value: StuffWrapper) -> Stuff { value.0 } } impl std::convert::From<&StuffWrapper> for Stuff { fn from(value: &StuffWrapper) -> Stuff { value.0.clone() } } impl std::convert::Into<StuffWrapper> for Stuff { fn into(self) -> StuffWrapper { StuffWrapper(self) } } impl std::convert::Into<StuffWrapper> for &Stuff { fn into(self) -> StuffWrapper { StuffWrapper(self.clone()) } }

Contributions

All issues, questions, pull requests are extremely welcome.

License

Licensed under either an Apache License, Version 2.0 or MIT license at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.