diff --git a/README.md b/README.md
index 1c489d2..02daf5c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Object to Object mapper for Rust. Derive `From` and `Into` traits.
+Object to Object mapper for Rust. Derive `(Try)From`, and `(Try)Into` traits.
================================
[](https://github.com/Artem-Romanenia/o2o/)
[](https://crates.io/crates/o2o)
@@ -33,7 +33,7 @@ struct Person {
#[derive(o2o)]
#[from_owned(Person)] // This tells o2o to generate 'From for PersonDto' implementation
-#[owned_into(Person)] // This generates 'Into for PersonDto'
+#[owned_try_into(Person, std::io::Error)] // This generates 'TryInto for PersonDto' with type Error = std::io::Error
struct PersonDto {
id: u32,
name: String,
@@ -50,11 +50,11 @@ assert_eq!(dto.id, 123); assert_eq!(dto.name, "John"); assert_eq!(dto.age, 42);
// and this:
let dto = PersonDto { id: 321, name: "Jack".into(), age: 23 };
-let person: Person = dto.into();
+let person: Person = dto.try_into().unwrap();
assert_eq!(person.id, 321); assert_eq!(person.name, "Jack"); assert_eq!(person.age, 23);
-// Starting from v0.4.2 o2o also supports enums:
+// o2o also supports enums:
enum Creature {
Person(Person),
@@ -66,7 +66,7 @@ enum Creature {
#[derive(o2o)]
#[from_owned(Creature)]
enum CreatureDto {
- Person(#[map(~.into())] PersonDto),
+ Person(#[from(~.into())] PersonDto),
Cat { nickname: String },
Dog(String),
Other
@@ -92,13 +92,14 @@ And here's the code that `o2o` generates (from here on, generated code is produc
}
}
}
- impl std::convert::Into for PersonDto {
- fn into(self) -> Person {
- Person {
+ impl std::convert::TryInto for PersonDto {
+ type Error = std::io::Error;
+ fn try_into(self) -> Result {
+ Ok(Person {
id: self.id,
name: self.name,
age: self.age,
- }
+ })
}
}
@@ -115,6 +116,13 @@ And here's the code that `o2o` generates (from here on, generated code is produc
```
+## Some milestones
+
+* **v0.4.4** Fallible conversions
+* **v0.4.3** Enum-to-primitive type conversions with `#[literal(...)]` and `#[pattern(...)]`
+* **v0.4.2** Basic enum conversions
+* **...**
+
## Content
- [Traits and `o2o` *trait instructions*](#traits-and-o2o-trait-instructions)
@@ -162,7 +170,7 @@ struct Entity { }
struct EntityDto { }
```
-o2o procedural macro is able to generate implementation of 6 kinds of traits:
+o2o procedural macro is able to generate implementation of 12 kinds of traits:
``` rust ignore
// When applied to a struct B:
@@ -170,20 +178,38 @@ o2o procedural macro is able to generate implementation of 6 kinds of traits:
// #[from_owned(A)]
impl std::convert::From for B { ... }
+// #[try_from_owned(A)]
+impl std::convert::TryFrom for B { ... }
+
// #[from_ref(A)]
impl std::convert::From<&A> for B { ... }
+// #[try_from_ref(A)]
+impl std::convert::TryFrom<&A> for B { ... }
+
// #[owned_into(A)]
impl std::convert::Into for B { ... }
+// #[try_owned_into(A)]
+impl std::convert::TryInto for B { ... }
+
// #[ref_into(A)]
impl std::convert::Into for &B { ... }
+// #[try_ref_into(A)]
+impl std::convert::TryInto for &B { ... }
+
// #[owned_into_existing(A)]
impl o2o::traits::IntoExisting for B { ... }
+// #[owned_try_into_existing(A)]
+impl o2o::traits::TryIntoExisting for B { ... }
+
// #[ref_into_existing(A)]
impl o2o::traits::IntoExisting for &B { ... }
+
+// #[ref_try_into_existing(A)]
+impl o2o::traits::TryIntoExisting for &B { ... }
```
o2o also has shortcuts to configure multiple trait implementations with fewer lines of code:
@@ -218,6 +244,8 @@ struct Entity { }
struct EntityDto { }
```
+**Exactly the same shortcuts apply to *fallible* conversions.**
+
## The (not so big) Problem
This section may be useful for people which are not very familiar with Rust's procedural macros and it explains why some things are done the way they're done.
@@ -313,7 +341,7 @@ impl std::convert::Into for EntityDto {
}
```
-Obviously, you can use `~` for inline expressions that are passed only to member level o2o instructions, while `@` can be used at both member and type level.
+You can use `~` for inline expressions that are passed only to member level o2o instructions, while `@` can be used at both member and type level.
So finally, let's look at some examples.
@@ -403,13 +431,14 @@ struct Entity {
}
#[derive(o2o)]
-#[map(Entity)]
+#[from(Entity)]
+#[try_into(Entity, std::num::ParseIntError)]
struct EntityDto {
some_int: i32,
#[map_ref(@.str.clone())]
str: String,
#[from(~.to_string())]
- #[into(~.parse::().unwrap())]
+ #[into(~.parse::()?)]
val: String
}
```
@@ -435,22 +464,24 @@ struct EntityDto {
}
}
}
- impl std::convert::Into for EntityDto {
- fn into(self) -> Entity {
- Entity {
+ impl std::convert::TryInto for EntityDto {
+ type Error = std::num::ParseIntError;
+ fn try_into(self) -> Result {
+ Ok(Entity {
some_int: self.some_int,
str: self.str,
- val: self.val.parse::().unwrap(),
- }
+ val: self.val.parse::()?,
+ })
}
}
- impl std::convert::Into for &EntityDto {
- fn into(self) -> Entity {
- Entity {
+ impl std::convert::TryInto for &EntityDto {
+ type Error = std::num::ParseIntError;
+ fn try_into(self) -> Result {
+ Ok(Entity {
some_int: self.some_int,
str: self.str.clone(),
- val: self.val.parse::().unwrap(),
- }
+ val: self.val.parse::()?,
+ })
}
}
```
@@ -776,12 +807,19 @@ use o2o::o2o;
#[derive(o2o)]
#[owned_into(String| return @.0.to_string())]
-struct Wrapper(i32);
+#[try_from_owned(String, std::num::ParseIntError)]
+struct Wrapper(#[from(@.parse::()?)]i32);
```
View generated code
``` rust ignore
+ impl std::convert::TryFrom for Wrapper {
+ type Error = std::num::ParseIntError;
+ fn try_from(value: String) -> Result {
+ Ok(Wrapper(value.parse::()?))
+ }
+ }
impl std::convert::Into for Wrapper {
fn into(self) -> String {
self.0.to_string()
@@ -1386,34 +1424,39 @@ struct Entity {
}
// =====================================================================
#[derive(o2o)]
-#[map(Entity)]
+#[from(Entity)]
+#[try_into(Entity, std::num::ParseIntError)]
struct EntityDto1 {
some_int: i32,
#[from(~.to_string())]
- #[into(~.parse::().unwrap())]
+ #[into(~.parse::()?)]
val: String,
#[map_ref(~.clone())]
str: String
}
// =====================================================================
#[derive(o2o)]
-#[o2o(map(Entity))]
+#[o2o(from(Entity))]
+#[o2o(try_into(Entity, std::num::ParseIntError))]
struct EntityDto2 {
some_int: i32,
#[o2o(from(~.to_string()))]
- #[o2o(into(~.parse::().unwrap()))]
+ #[o2o(into(~.parse::()?))]
val: String,
#[o2o(map_ref(~.clone()))]
str: String
}
// =====================================================================
#[derive(o2o)]
-#[o2o(map(Entity))]
+#[o2o(
+ from(Entity),
+ try_into(Entity, std::num::ParseIntError)
+)]
struct EntityDto3 {
some_int: i32,
#[o2o(
from(~.to_string()),
- into(~.parse::().unwrap()),
+ try_into(~.parse::()?),
)]
val: String,
#[o2o(map_ref(~.clone()))]
diff --git a/o2o-impl/src/attr.rs b/o2o-impl/src/attr.rs
index 74485f6..778deb2 100644
--- a/o2o-impl/src/attr.rs
+++ b/o2o-impl/src/attr.rs
@@ -174,8 +174,8 @@ pub(crate) struct DataTypeAttrs {
}
impl<'a> DataTypeAttrs {
- pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind) -> impl Iterator- {
- self.attrs.iter().filter(move |x| x.applicable_to[kind]).map(|x| &x.attr)
+ pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind, fallible: bool) -> impl Iterator
- {
+ self.attrs.iter().filter(move |x| x.fallible == fallible && x.applicable_to[kind]).map(|x| &x.attr)
}
pub(crate) fn ghost_attr(&'a self, container_ty: &'a TypePath, kind: &'a Kind) -> Option<&StructGhostAttrCore> {
@@ -259,27 +259,30 @@ pub(crate) struct MemberAttrs {
}
impl<'a> MemberAttrs {
- pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind) -> impl Iterator
- {
- self.attrs.iter().filter(move |x| x.applicable_to[kind])
+ pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind, fallible: bool) -> impl Iterator
- {
+ self.attrs.iter().filter(move |x| x.fallible == fallible && x.applicable_to[kind])
}
- pub(crate) fn iter_for_kind_core(&'a self, kind: &'a Kind) -> impl Iterator
- {
- self.iter_for_kind(kind).map(|x| &x.attr)
+ pub(crate) fn iter_for_kind_core(&'a self, kind: &'a Kind, fallible: bool) -> impl Iterator
- {
+ self.iter_for_kind(kind, fallible).map(|x| &x.attr)
}
- pub(crate) fn applicable_attr(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option {
+ pub(crate) fn applicable_attr(&'a self, kind: &'a Kind, fallible: bool, container_ty: &TypePath) -> Option {
self.ghost(container_ty, kind)
.map(ApplicableAttr::Ghost)
- .or_else(|| self.field_attr_core(kind, container_ty)
- .or_else(|| if kind == &Kind::OwnedIntoExisting { self.field_attr_core(&Kind::OwnedInto, container_ty) } else { None })
- .or_else(|| if kind == &Kind::RefIntoExisting { self.field_attr_core(&Kind::RefInto, container_ty) } else { None })
+ .or_else(|| self.field_attr_core(kind, fallible, container_ty)
+ .or_else(|| if fallible { self.field_attr_core(kind, false, container_ty) } else { None })
+ .or_else(|| if kind == &Kind::OwnedIntoExisting { self.field_attr_core(&Kind::OwnedInto, fallible, container_ty) } else { None })
+ .or_else(|| if kind == &Kind::OwnedIntoExisting && fallible { self.field_attr_core(&Kind::OwnedInto, false, container_ty) } else { None })
+ .or_else(|| if kind == &Kind::RefIntoExisting { self.field_attr_core(&Kind::RefInto, fallible, container_ty) } else { None })
+ .or_else(|| if kind == &Kind::RefIntoExisting && fallible { self.field_attr_core(&Kind::RefInto, false, container_ty) } else { None })
.map(ApplicableAttr::Field))
}
- pub(crate) fn applicable_field_attr(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&'a MemberAttr> {
- self.field_attr(kind, container_ty)
- .or_else(|| if kind == &Kind::OwnedIntoExisting { self.field_attr(&Kind::OwnedInto, container_ty) } else { None })
- .or_else(|| if kind == &Kind::RefIntoExisting { self.field_attr(&Kind::RefInto, container_ty) } else { None })
+ pub(crate) fn applicable_field_attr(&'a self, kind: &'a Kind, fallible: bool, container_ty: &TypePath) -> Option<&'a MemberAttr> {
+ self.field_attr(kind, fallible, container_ty)
+ .or_else(|| if kind == &Kind::OwnedIntoExisting { self.field_attr(&Kind::OwnedInto, fallible, container_ty) } else { None })
+ .or_else(|| if kind == &Kind::RefIntoExisting { self.field_attr(&Kind::RefInto, fallible, container_ty) } else { None })
}
pub(crate) fn child(&'a self, container_ty: &TypePath) -> Option<&ChildAttr>{
@@ -311,16 +314,16 @@ impl<'a> MemberAttrs {
.any(|x| x.container_ty.is_none() || x.container_ty.as_ref().unwrap() == container_ty)
}
- pub(crate) fn field_attr(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&MemberAttr>{
- self.iter_for_kind(kind)
+ pub(crate) fn field_attr(&'a self, kind: &'a Kind, fallible: bool, container_ty: &TypePath) -> Option<&MemberAttr>{
+ self.iter_for_kind(kind, fallible)
.find(|x| x.attr.container_ty.is_some() && x.attr.container_ty.as_ref().unwrap() == container_ty)
- .or_else(|| self.iter_for_kind(kind).find(|x| x.attr.container_ty.is_none()))
+ .or_else(|| self.iter_for_kind(kind, fallible).find(|x| x.attr.container_ty.is_none()))
}
- pub(crate) fn field_attr_core(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&MemberAttrCore>{
- self.iter_for_kind_core(kind)
+ pub(crate) fn field_attr_core(&'a self, kind: &'a Kind, fallible: bool, container_ty: &TypePath) -> Option<&MemberAttrCore>{
+ self.iter_for_kind_core(kind, fallible)
.find(|x| x.container_ty.is_some() && x.container_ty.as_ref().unwrap() == container_ty)
- .or_else(|| self.iter_for_kind_core(kind).find(|x| x.container_ty.is_none()))
+ .or_else(|| self.iter_for_kind_core(kind, fallible).find(|x| x.container_ty.is_none()))
}
pub(crate) fn merge(&'a mut self, other: Self) {
@@ -351,12 +354,14 @@ pub(crate) enum TypeHint {
#[derive(Clone)]
pub(crate) struct TraitAttr {
pub attr: TraitAttrCore,
+ pub fallible: bool,
pub applicable_to: ApplicableTo,
}
#[derive(Clone)]
pub(crate) struct TraitAttrCore {
pub ty: TypePath,
+ pub err_ty: Option,
pub type_hint: TypeHint,
pub init_data: Option>,
pub update: Option,
@@ -373,9 +378,15 @@ impl Parse for TraitAttrCore {
quote!((#content_stream)).into()
} else { input.parse::()?.into() };
let type_hint = if ty.nameless_tuple { TypeHint::Tuple } else { try_parse_type_hint(input)? };
+ let err_ty = if input.peek(Token![,]) {
+ input.parse::()?;
+ Some(input.parse::()?.into())
+ } else {
+ None
+ };
if !input.peek(Token![|]){
- return Ok(TraitAttrCore { ty, type_hint, init_data: None, update: None, quick_return: None, default_case: None })
+ return Ok(TraitAttrCore { ty, err_ty, type_hint, init_data: None, update: None, quick_return: None, default_case: None })
}
input.parse::()?;
@@ -403,7 +414,7 @@ impl Parse for TraitAttrCore {
try_parse_action(input, true)?
} else { None };
- Ok(TraitAttrCore { ty, type_hint, init_data, update, quick_return, default_case })
+ Ok(TraitAttrCore { ty, err_ty, type_hint, init_data, update, quick_return, default_case })
}
}
@@ -546,6 +557,7 @@ impl Hash for ChildData {
#[derive(Clone)]
pub(crate) struct MemberAttr {
pub attr: MemberAttrCore,
+ pub fallible: bool,
pub original_instr: String,
applicable_to: ApplicableTo,
}
@@ -779,16 +791,32 @@ fn parse_data_type_instruction(instr: &Ident, input: TokenStream, own_instr: boo
"owned_into" | "ref_into" | "into" | "from_owned" | "from_ref" | "from" |
"map_owned" | "map_ref" | "map" | "owned_into_existing" | "ref_into_existing" | "into_existing" =>
Ok(DataTypeInstruction::Map(TraitAttr {
- attr: syn::parse2(input)?,
+ attr: syn::parse2(input)?,
+ fallible: false,
applicable_to: [
- appl_owned_into(instr_str),
- appl_ref_into(instr_str),
- appl_from_owned(instr_str),
- appl_from_ref(instr_str),
+ appl_owned_into(instr_str),
+ appl_ref_into(instr_str),
+ appl_from_owned(instr_str),
+ appl_from_ref(instr_str),
appl_owned_into_existing(instr_str),
appl_ref_into_existing(instr_str)
]
})),
+ "owned_try_into" | "ref_try_into" | "try_into" | "try_from_owned" | "try_from_ref" | "try_from" |
+ "try_map_owned" | "try_map_ref" | "try_map" | "owned_try_into_existing" | "ref_try_into_existing" | "try_into_existing" => {
+ Ok(DataTypeInstruction::Map(TraitAttr {
+ attr: syn::parse2(input)?,
+ fallible: true,
+ applicable_to: [
+ appl_owned_into(instr_str),
+ appl_ref_into(instr_str),
+ appl_from_owned(instr_str),
+ appl_from_ref(instr_str),
+ appl_owned_into_existing(instr_str),
+ appl_ref_into_existing(instr_str)
+ ]
+ }))
+ },
"ghosts" | "ghosts_ref" | "ghosts_owned" => Ok(DataTypeInstruction::Ghosts(GhostsAttr {
attr: syn::parse2(input)?,
applicable_to: [
@@ -823,13 +851,29 @@ fn parse_member_instruction(instr: &Ident, input: TokenStream, own_instr: bool,
"owned_into" | "ref_into" | "into" | "from_owned" | "from_ref" | "from" |
"map_owned" | "map_ref" | "map" | "owned_into_existing" | "ref_into_existing" | "into_existing" =>
Ok(MemberInstruction::Map(MemberAttr {
- attr: syn::parse2(input)?,
+ attr: syn::parse2(input)?,
+ fallible: false,
original_instr: instr_str.clone(),
applicable_to: [
- appl_owned_into(instr_str),
- appl_ref_into(instr_str),
- appl_from_owned(instr_str),
- appl_from_ref(instr_str),
+ appl_owned_into(instr_str),
+ appl_ref_into(instr_str),
+ appl_from_owned(instr_str),
+ appl_from_ref(instr_str),
+ appl_owned_into_existing(instr_str),
+ appl_ref_into_existing(instr_str)
+ ]
+ })),
+ "owned_try_into" | "ref_try_into" | "try_into" | "try_from_owned" | "try_from_ref" | "try_from" |
+ "try_map_owned" | "try_map_ref" | "try_map" =>
+ Ok(MemberInstruction::Map(MemberAttr {
+ attr: syn::parse2(input)?,
+ fallible: true,
+ original_instr: instr_str.clone(),
+ applicable_to: [
+ appl_owned_into(instr_str),
+ appl_ref_into(instr_str),
+ appl_from_owned(instr_str),
+ appl_from_ref(instr_str),
appl_owned_into_existing(instr_str),
appl_ref_into_existing(instr_str)
]
@@ -975,10 +1019,11 @@ fn add_as_type_attrs(input: &syn::Field, attr: AsAttr, attrs: &mut Vec bool {
- matches!(instr, "owned_into" | "into" | "map_owned" | "map")
+ matches!(instr, "owned_into" | "into" | "map_owned" | "map" | "owned_try_into" | "try_into" | "try_map_owned" | "try_map")
}
fn appl_ref_into(instr: &str) -> bool {
- matches!(instr, "ref_into" | "into" | "map_ref" | "map")
+ matches!(instr, "ref_into" | "into" | "map_ref" | "map" | "ref_try_into" | "try_into" | "try_map_ref" | "try_map")
}
fn appl_from_owned(instr: &str) -> bool {
- matches!(instr, "from_owned" | "from" | "map_owned" | "map")
+ matches!(instr, "from_owned" | "from" | "map_owned" | "map" | "try_from_owned" | "try_from" | "try_map_owned" | "try_map")
}
fn appl_from_ref(instr: &str) -> bool {
- matches!(instr, "from_ref" | "from" | "map_ref" | "map")
+ matches!(instr, "from_ref" | "from" | "map_ref" | "map" | "try_from_ref" | "try_from" | "try_map_ref" | "try_map")
}
fn appl_owned_into_existing(instr: &str) -> bool {
- matches!(instr, "owned_into_existing" | "into_existing")
+ matches!(instr, "owned_into_existing" | "into_existing" | "owned_try_into_existing" | "try_into_existing")
}
fn appl_ref_into_existing(instr: &str) -> bool {
- matches!(instr, "ref_into_existing" | "into_existing")
+ matches!(instr, "ref_into_existing" | "into_existing" | "ref_try_into_existing" | "try_into_existing")
}
fn appl_ghosts_owned(instr: &str) -> bool {
diff --git a/o2o-impl/src/expand.rs b/o2o-impl/src/expand.rs
index 58386e0..829952f 100644
--- a/o2o-impl/src/expand.rs
+++ b/o2o-impl/src/expand.rs
@@ -2,7 +2,7 @@ use std::{slice::Iter, iter::Peekable, collections::HashSet};
use crate::{
ast::{DataType, DataTypeMember, Enum, Field, Struct, Variant},
- attr::{ApplicableAttr, ChildData, ChildPath, GhostData, InitData, Kind, TraitAttrCore, DataTypeAttrs, TypeHint}, validate::validate
+ attr::{ApplicableAttr, ChildData, ChildPath, DataTypeAttrs, GhostData, InitData, Kind, TraitAttrCore, TypeHint}, validate::validate
};
use proc_macro2::{TokenStream, Span};
use syn::{punctuated::Punctuated, Data, DeriveInput, Error, Generics, Index, Member, Result, Token};
@@ -29,7 +29,6 @@ pub fn derive(node: &DeriveInput) -> Result {
}
}
-#[derive(Clone)]
struct ImplContext<'a> {
input: &'a DataType<'a>,
struct_attr: &'a TraitAttrCore,
@@ -38,13 +37,14 @@ struct ImplContext<'a> {
src_ty: &'a TokenStream,
has_post_init: bool,
destructured_src: bool,
+ fallible: bool
}
fn struct_impl(input: DataType) -> TokenStream {
let ty = input.get_ident().to_token_stream();
let attrs = input.get_attrs();
- let from_owned_impls = attrs.iter_for_kind(&Kind::FromOwned).map(|struct_attr| {
+ let from_owned_impls = attrs.iter_for_kind(&Kind::FromOwned, false).map(|struct_attr| {
let ctx = ImplContext {
input: &input,
struct_attr,
@@ -52,7 +52,8 @@ fn struct_impl(input: DataType) -> TokenStream {
dst_ty: &ty,
src_ty: &struct_attr.ty.path,
has_post_init: false,
- destructured_src: false
+ destructured_src: false,
+ fallible: false,
};
let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
@@ -62,7 +63,26 @@ fn struct_impl(input: DataType) -> TokenStream {
quote_from_trait(&ctx, pre_init, main_code_block(&ctx))
});
- let from_ref_impls = attrs.iter_for_kind(&Kind::FromRef).map(|struct_attr| {
+ let try_from_owned_impls = attrs.iter_for_kind(&Kind::FromOwned, true).map(|struct_attr| {
+ let ctx = ImplContext {
+ input: &input,
+ struct_attr,
+ kind: Kind::FromOwned,
+ dst_ty: &ty,
+ src_ty: &struct_attr.ty.path,
+ has_post_init: false,
+ destructured_src: false,
+ fallible: true,
+ };
+
+ let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ if let Some(quick_return) = &struct_attr.quick_return {
+ return quote_try_from_trait(&ctx, pre_init, quote_action(quick_return, None, &ctx))
+ }
+ quote_try_from_trait(&ctx, pre_init, main_code_block_ok(&ctx))
+ });
+
+ let from_ref_impls = attrs.iter_for_kind(&Kind::FromRef, false).map(|struct_attr| {
let ctx = ImplContext {
input: &input,
struct_attr,
@@ -70,7 +90,8 @@ fn struct_impl(input: DataType) -> TokenStream {
dst_ty: &ty,
src_ty: &struct_attr.ty.path,
has_post_init: false,
- destructured_src: false
+ destructured_src: false,
+ fallible: false,
};
let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
@@ -80,7 +101,26 @@ fn struct_impl(input: DataType) -> TokenStream {
quote_from_trait(&ctx, pre_init, main_code_block(&ctx))
});
- let owned_into_impls = attrs.iter_for_kind(&Kind::OwnedInto).map(|struct_attr| {
+ let try_from_ref_impls = attrs.iter_for_kind(&Kind::FromRef, true).map(|struct_attr| {
+ let ctx = ImplContext {
+ input: &input,
+ struct_attr,
+ kind: Kind::FromRef,
+ dst_ty: &ty,
+ src_ty: &struct_attr.ty.path,
+ has_post_init: false,
+ destructured_src: false,
+ fallible: true,
+ };
+
+ let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ if let Some(quick_return) = &struct_attr.quick_return {
+ return quote_try_from_trait(&ctx, pre_init, quote_action(quick_return, None, &ctx))
+ }
+ quote_try_from_trait(&ctx, pre_init, main_code_block_ok(&ctx))
+ });
+
+ let owned_into_impls = attrs.iter_for_kind(&Kind::OwnedInto, false).map(|struct_attr| {
let mut ctx = ImplContext {
input: &input,
struct_attr,
@@ -88,7 +128,8 @@ fn struct_impl(input: DataType) -> TokenStream {
dst_ty: &struct_attr.ty.path,
src_ty: &ty,
has_post_init: false,
- destructured_src: false
+ destructured_src: false,
+ fallible: false,
};
let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
@@ -100,7 +141,28 @@ fn struct_impl(input: DataType) -> TokenStream {
quote_into_trait(&ctx, pre_init, main_code_block(&ctx), post_init)
});
- let ref_into_impls = attrs.iter_for_kind(&Kind::RefInto).map(|struct_attr| {
+ let owned_try_into_impls = attrs.iter_for_kind(&Kind::OwnedInto, true).map(|struct_attr| {
+ let mut ctx = ImplContext {
+ input: &input,
+ struct_attr,
+ kind: Kind::OwnedInto,
+ dst_ty: &struct_attr.ty.path,
+ src_ty: &ty,
+ has_post_init: false,
+ destructured_src: false,
+ fallible: true,
+ };
+
+ let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ if let Some(quick_return) = &struct_attr.quick_return {
+ return quote_try_into_trait(&ctx, pre_init, quote_action(quick_return, None, &ctx), None)
+ }
+ let post_init = struct_post_init(&ctx);
+ ctx.has_post_init = post_init.is_some();
+ quote_try_into_trait(&ctx, pre_init, main_code_block_ok(&ctx), post_init)
+ });
+
+ let ref_into_impls = attrs.iter_for_kind(&Kind::RefInto, false).map(|struct_attr| {
let mut ctx = ImplContext {
input: &input,
struct_attr,
@@ -108,7 +170,8 @@ fn struct_impl(input: DataType) -> TokenStream {
dst_ty: &struct_attr.ty.path,
src_ty: &ty,
has_post_init: false,
- destructured_src: false
+ destructured_src: false,
+ fallible: false,
};
let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
@@ -120,7 +183,28 @@ fn struct_impl(input: DataType) -> TokenStream {
quote_into_trait(&ctx, pre_init, main_code_block(&ctx), post_init)
});
- let owned_into_existing_impls = attrs.iter_for_kind(&Kind::OwnedIntoExisting).map(|struct_attr| {
+ let ref_try_into_impls = attrs.iter_for_kind(&Kind::RefInto, true).map(|struct_attr| {
+ let mut ctx = ImplContext {
+ input: &input,
+ struct_attr,
+ kind: Kind::RefInto,
+ dst_ty: &struct_attr.ty.path,
+ src_ty: &ty,
+ has_post_init: false,
+ destructured_src: false,
+ fallible: true,
+ };
+
+ let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ if let Some(quick_return) = &struct_attr.quick_return {
+ return quote_try_into_trait(&ctx, pre_init, quote_action(quick_return, None, &ctx), None)
+ }
+ let post_init = struct_post_init(&ctx);
+ ctx.has_post_init = post_init.is_some();
+ quote_try_into_trait(&ctx, pre_init, main_code_block_ok(&ctx), post_init)
+ });
+
+ let owned_into_existing_impls = attrs.iter_for_kind(&Kind::OwnedIntoExisting, false).map(|struct_attr| {
let mut ctx = ImplContext {
input: &input,
struct_attr,
@@ -128,9 +212,11 @@ fn struct_impl(input: DataType) -> TokenStream {
dst_ty: &struct_attr.ty.path,
src_ty: &ty,
has_post_init: false,
- destructured_src: false
+ destructured_src: false,
+ fallible: false,
};
let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ //TODO: Consider removing quick returns for into_existing because they are confusing
if let Some(quick_return) = &struct_attr.quick_return {
let action = quote_action(quick_return, None, &ctx);
return quote_into_existing_trait(&ctx, pre_init, quote!(*other = #action;), None)
@@ -140,7 +226,29 @@ fn struct_impl(input: DataType) -> TokenStream {
quote_into_existing_trait(&ctx, pre_init, main_code_block(&ctx), post_init)
});
- let ref_into_existing_impls = attrs.iter_for_kind(&Kind::RefIntoExisting).map(|struct_attr| {
+ let owned_try_into_existing_impls = attrs.iter_for_kind(&Kind::OwnedIntoExisting, true).map(|struct_attr| {
+ let mut ctx = ImplContext {
+ input: &input,
+ struct_attr,
+ kind: Kind::OwnedIntoExisting,
+ dst_ty: &struct_attr.ty.path,
+ src_ty: &ty,
+ has_post_init: false,
+ destructured_src: false,
+ fallible: true,
+ };
+ let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ //TODO: Consider removing quick returns for into_existing because they are confusing
+ if let Some(quick_return) = &struct_attr.quick_return {
+ let action = quote_action(quick_return, None, &ctx);
+ return quote_try_into_existing_trait(&ctx, pre_init, quote!(*other = #action;), None)
+ }
+ let post_init = struct_post_init(&ctx);
+ ctx.has_post_init = post_init.is_some();
+ quote_try_into_existing_trait(&ctx, pre_init, main_code_block(&ctx), post_init)
+ });
+
+ let ref_into_existing_impls = attrs.iter_for_kind(&Kind::RefIntoExisting, false).map(|struct_attr| {
let mut ctx = ImplContext {
input: &input,
struct_attr,
@@ -148,9 +256,11 @@ fn struct_impl(input: DataType) -> TokenStream {
dst_ty: &struct_attr.ty.path,
src_ty: &ty,
has_post_init: false,
- destructured_src: false
+ destructured_src: false,
+ fallible: false,
};
let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ //TODO: Consider removing quick returns for into_existing because they are confusing
if let Some(quick_return) = &struct_attr.quick_return {
let action = quote_action(quick_return, None, &ctx);
return quote_into_existing_trait(&ctx, pre_init, quote!(*other = #action;), None)
@@ -160,18 +270,56 @@ fn struct_impl(input: DataType) -> TokenStream {
quote_into_existing_trait(&ctx, pre_init, main_code_block(&ctx), post_init)
});
+ let ref_try_into_existing_impls = attrs.iter_for_kind(&Kind::RefIntoExisting, true).map(|struct_attr| {
+ let mut ctx = ImplContext {
+ input: &input,
+ struct_attr,
+ kind: Kind::RefIntoExisting,
+ dst_ty: &struct_attr.ty.path,
+ src_ty: &ty,
+ has_post_init: false,
+ destructured_src: false,
+ fallible: true,
+ };
+ let pre_init = struct_pre_init(&ctx, &struct_attr.init_data);
+ //TODO: Consider removing quick returns for into_existing because they are confusing
+ if let Some(quick_return) = &struct_attr.quick_return {
+ let action = quote_action(quick_return, None, &ctx);
+ return quote_try_into_existing_trait(&ctx, pre_init, quote!(*other = #action;), None)
+ }
+ let post_init = struct_post_init(&ctx);
+ ctx.has_post_init = post_init.is_some();
+ quote_try_into_existing_trait(&ctx, pre_init, main_code_block(&ctx), post_init)
+ });
+
let result = quote! {
#(#from_owned_impls)*
+ #(#try_from_owned_impls)*
#(#from_ref_impls)*
+ #(#try_from_ref_impls)*
#(#owned_into_impls)*
+ #(#owned_try_into_impls)*
#(#ref_into_impls)*
+ #(#ref_try_into_impls)*
#(#owned_into_existing_impls)*
+ #(#owned_try_into_existing_impls)*
#(#ref_into_existing_impls)*
+ #(#ref_try_into_existing_impls)*
};
result
}
+fn main_code_block_ok(ctx: &ImplContext) -> TokenStream {
+ let inner = main_code_block(ctx);
+
+ if ctx.has_post_init {
+ quote!(#inner)
+ } else {
+ quote!(Ok(#inner))
+ }
+}
+
fn main_code_block(ctx: &ImplContext) -> TokenStream {
match ctx.input {
DataType::Struct(_) => {
@@ -376,7 +524,7 @@ fn variant_destruct_block(ctx: &ImplContext<'_>) -> TokenStream {
DataType::Struct(s) => {
if s.named_fields {
let idents = s.fields.iter().map(|x| {
- let attr = x.attrs.applicable_attr(&ctx.kind, &ctx.struct_attr.ty);
+ let attr = x.attrs.applicable_attr(&ctx.kind, ctx.fallible, &ctx.struct_attr.ty);
if !ctx.kind.is_from() || attr.is_none() {
let ident = &x.member;
@@ -473,11 +621,15 @@ fn struct_post_init(ctx: &ImplContext) -> Option {
fn render_parent(f: &Field, ctx: &ImplContext) -> TokenStream {
let member = &f.member;
- match &ctx.kind {
- Kind::OwnedIntoExisting => quote!(self.#member.into_existing(other);),
- Kind::RefIntoExisting => quote!((&(self.#member)).into_existing(other);),
- Kind::OwnedInto => quote!(self.#member.into_existing(&mut obj);),
- Kind::RefInto => quote!((&(self.#member)).into_existing(&mut obj);),
+ match (&ctx.kind, ctx.fallible) {
+ (Kind::OwnedIntoExisting, false) => quote!(self.#member.into_existing(other);),
+ (Kind::RefIntoExisting, false) => quote!((&(self.#member)).into_existing(other);),
+ (Kind::OwnedInto, false) => quote!(self.#member.into_existing(&mut obj);),
+ (Kind::RefInto, false) => quote!((&(self.#member)).into_existing(&mut obj);),
+ (Kind::OwnedIntoExisting, true) => quote!(self.#member.try_into_existing(other)?;),
+ (Kind::RefIntoExisting, true) => quote!((&(self.#member)).try_into_existing(other)?;),
+ (Kind::OwnedInto, true) => quote!(self.#member.try_into_existing(&mut obj)?;),
+ (Kind::RefInto, true) => quote!((&(self.#member)).try_into_existing(&mut obj)?;),
_ => panic!("weird")
}
}
@@ -533,7 +685,7 @@ fn render_struct_line(
hint: TypeHint,
idx: usize
) -> TokenStream {
- let attr = f.attrs.applicable_attr(&ctx.kind, &ctx.struct_attr.ty);
+ let attr = f.attrs.applicable_attr(&ctx.kind, ctx.fallible, &ctx.struct_attr.ty);
let get_field_path = |x: &Member| {
match f.attrs.child(&ctx.struct_attr.ty) {
Some(child_attr) => {
@@ -570,7 +722,12 @@ fn render_struct_line(
}
(syn::Member::Named(ident), None, Kind::FromOwned | Kind::FromRef, TypeHint::Struct | TypeHint::Unspecified) =>
if f.attrs.has_parent_attr(&ctx.struct_attr.ty) {
- if !ctx.kind.is_ref() { quote!(#ident: (&value).into(),) } else { quote!(#ident: value.into(),) }
+ match (ctx.kind.is_ref(), ctx.fallible) {
+ (true, true) => quote!(#ident: value.try_into()?,),
+ (true, false) => quote!(#ident: value.into(),),
+ (false, true) => quote!(#ident: (&value).try_into()?,),
+ (false, false) => quote!(#ident: (&value).into(),),
+ }
} else {
let field_path = get_field_path(&f.member);
quote!(#ident: #obj #field_path,)
@@ -594,14 +751,24 @@ fn render_struct_line(
}
(syn::Member::Unnamed(index), None, Kind::FromOwned | Kind::FromRef, TypeHint::Tuple | TypeHint::Unspecified) =>
if f.attrs.has_parent_attr(&ctx.struct_attr.ty) {
- if !ctx.kind.is_ref() { quote!((&value).into(),) } else { quote!(value.into(),) }
+ match (ctx.kind.is_ref(), ctx.fallible) {
+ (true, true) => quote!(value.try_into()?,),
+ (true, false) => quote!(value.into(),),
+ (false, true) => quote!((&value).try_into()?,),
+ (false, false) => quote!((&value).into(),),
+ }
} else {
let field_path = if ctx.destructured_src { get_field_path(&Member::Named(format_ident!("f{}", index.index))) } else { get_field_path(&f.member) };
quote!(#obj #field_path,)
},
(syn::Member::Unnamed(_), None, _, TypeHint::Struct) => {
if f.attrs.has_parent_attr(&ctx.struct_attr.ty) {
- if !ctx.kind.is_ref() { quote!((&value).into(),) } else { quote!(value.into(),) }
+ match (ctx.kind.is_ref(), ctx.fallible) {
+ (true, true) => quote!(value.try_into()?,),
+ (true, false) => quote!(value.into(),),
+ (false, true) => quote!((&value).try_into()?,),
+ (false, false) => quote!((&value).into(),),
+ }
} else {
panic!("weird")
}
@@ -653,7 +820,7 @@ fn render_struct_line(
let or = Member::Named(format_ident!("f{}", index.index));
let right_side = attr.get_stuff(&obj, get_field_path, ctx, || if ctx.destructured_src { &or } else { &f.member});
quote!(#right_side,)
- }
+ },
}
}
@@ -663,7 +830,7 @@ fn render_enum_line(
hint: TypeHint,
_idx: usize
) -> TokenStream {
- let attr = v.attrs.applicable_attr(&ctx.kind, &ctx.struct_attr.ty);
+ let attr = v.attrs.applicable_attr(&ctx.kind, ctx.fallible, &ctx.struct_attr.ty);
let lit = v.attrs.lit(&ctx.struct_attr.ty);
let pat = v.attrs.pat(&ctx.struct_attr.ty);
@@ -821,6 +988,28 @@ fn quote_from_trait(ctx: &ImplContext, pre_init: Option, init: Toke
}
}
+fn quote_try_from_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream) -> TokenStream {
+ let dst = ctx.dst_ty;
+ let src = ctx.src_ty;
+ let err_ty = &ctx.struct_attr.err_ty.as_ref().unwrap().path;
+ let gens = ctx.input.get_generics().to_token_stream();
+ let where_clause = ctx.input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x| {
+ let where_clause = x.where_clause.to_token_stream();
+ quote!(where #where_clause)
+ });
+ let r = ctx.kind.is_ref().then_some(quote!(&));
+ quote! {
+ impl #gens std::convert::TryFrom<#r #src> for #dst #gens #where_clause {
+ type Error = #err_ty;
+
+ fn try_from(value: #r #src) -> Result<#dst #gens, #err_ty> {
+ #pre_init
+ #init
+ }
+ }
+ }
+}
+
fn quote_into_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream {
let dst = ctx.dst_ty;
let src = ctx.src_ty;
@@ -852,6 +1041,40 @@ fn quote_into_trait(ctx: &ImplContext, pre_init: Option, init: Toke
}
}
+fn quote_try_into_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream {
+ let dst = ctx.dst_ty;
+ let src = ctx.src_ty;
+ let err_ty = &ctx.struct_attr.err_ty.as_ref().unwrap().path;
+ let gens = ctx.input.get_generics().to_token_stream();
+ let where_clause = ctx.input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x|{
+ let where_clause = x.where_clause.to_token_stream();
+ quote!(where #where_clause)
+ });
+ let r = ctx.kind.is_ref().then_some(quote!(&));
+ match post_init {
+ Some(post_init) => quote!{
+ impl #gens std::convert::TryInto<#dst> for #r #src #gens #where_clause {
+ type Error = #err_ty;
+ fn try_into(self) -> Result<#dst, #err_ty> {
+ let mut obj: #dst = Default::default();
+ #init
+ #post_init
+ Ok(obj)
+ }
+ }
+ },
+ None => quote! {
+ impl #gens std::convert::TryInto<#dst> for #r #src #gens #where_clause {
+ type Error = #err_ty;
+ fn try_into(self) -> Result<#dst, #err_ty> {
+ #pre_init
+ #init
+ }
+ }
+ }
+ }
+}
+
fn quote_into_existing_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream {
let dst = ctx.dst_ty;
let src = ctx.src_ty;
@@ -872,6 +1095,29 @@ fn quote_into_existing_trait(ctx: &ImplContext, pre_init: Option, i
}
}
+fn quote_try_into_existing_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream {
+ let dst = ctx.dst_ty;
+ let src = ctx.src_ty;
+ let err_ty = &ctx.struct_attr.err_ty.as_ref().unwrap().path;
+ let gens = ctx.input.get_generics().to_token_stream();
+ let where_clause = ctx.input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x|{
+ let where_clause = x.where_clause.to_token_stream();
+ quote!(where #where_clause)
+ });
+ let r = ctx.kind.is_ref().then_some(quote!(&));
+ quote! {
+ impl #gens o2o::traits::TryIntoExisting<#dst> for #r #src #gens #where_clause {
+ type Error = #err_ty;
+ fn try_into_existing(self, other: &mut #dst) -> Result<(), #err_ty> {
+ #pre_init
+ #init
+ #post_init
+ Ok(())
+ }
+ }
+ }
+}
+
impl<'a> ApplicableAttr<'a> {
fn get_ident(&self) -> &'a Member {
match self {
diff --git a/o2o-impl/src/tests.rs b/o2o-impl/src/tests.rs
index c5cf11e..38c83be 100644
--- a/o2o-impl/src/tests.rs
+++ b/o2o-impl/src/tests.rs
@@ -425,7 +425,7 @@ fn unrecognized_struct_instructions_no_bark(code_fragment: TokenStream) {
}
}; "struct_misplaced_ghosts_instr")]
#[test_case(quote!{
- #[from_owned(NamedStruct)]
+ #[try_from_owned(NamedStruct, SomeError)]
#[o2o(allow_unknown)]
struct NamedStructDto {
#[ghosts_owned()]
@@ -517,8 +517,8 @@ fn more_than_one_default_instruction(code_fragment: TokenStream, err: &str) {
("EntityModel", true)
]; "1")]
#[test_case(quote! {
- #[map(EntityDto)]
- #[map(EntityModel)]
+ #[try_map(EntityDto, SomeError)]
+ #[try_map(EntityModel, SomeError)]
#[children()]
struct Entity {
#[child(base.base)]
@@ -532,7 +532,7 @@ fn more_than_one_default_instruction(code_fragment: TokenStream, err: &str) {
]; "2")]
#[test_case(quote! {
#[map(EntityDto)]
- #[map(EntityModel)]
+ #[try_map(EntityModel, SomeError)]
#[children(EntityDto| base: Base)]
struct Entity {
#[child(base.base)]
@@ -559,7 +559,7 @@ fn missing_children_instruction(code_fragment: TokenStream, errs: Vec<(&str, boo
#[test_case(quote! {
#[map(EntityDto)]
- #[map(EntityModel)]
+ #[try_map(EntityModel, SomeError)]
#[children()]
struct Entity {
#[child(base.base)]
@@ -577,7 +577,7 @@ fn missing_children_instruction(code_fragment: TokenStream, errs: Vec<(&str, boo
]; "1")]
#[test_case(quote! {
#[map(EntityDto)]
- #[map(EntityModel)]
+ #[try_map(EntityModel, SomeError)]
#[children(base: Base)]
struct Entity {
#[child(base.base)]
@@ -664,7 +664,7 @@ fn incomplete_children_instruction(code_fragment: TokenStream, errs: Vec<(&str,
("another_val", "Entity")
]; "4")]
#[test_case(quote! {
- #[map(Entity)]
+ #[try_map(Entity, SomeError)]
struct EntityDto {
some_val: i32,
#[ghost()]
@@ -675,7 +675,7 @@ fn incomplete_children_instruction(code_fragment: TokenStream, errs: Vec<(&str,
]; "5")]
#[test_case(quote! {
#[from(Entity)]
- #[from(Entity2)]
+ #[try_from(Entity2, SomeError)]
struct EntityDto {
some_val: i32,
#[ghost]
@@ -713,7 +713,7 @@ fn incomplete_ghost_instruction(code_fragment: TokenStream, errs: Vec<(&str, &st
("ref_into", "Entity", false)
]; "1")]
#[test_case(quote! {
- #[into(Entity as {})]
+ #[try_into(Entity as {}, SomeError)]
struct EntityDto (#[from]i32);
}, vec![
("owned_into", "Entity", false),
@@ -752,7 +752,7 @@ fn incomplete_ghost_instruction(code_fragment: TokenStream, errs: Vec<(&str, &st
]; "6")]
#[test_case(quote! {
#[map(Entity as {})]
- #[from(Entity2 as {})]
+ #[try_from(Entity2 as {}, SomeError)]
struct EntityDto (#[from(Entity2| {123})]i32);
}, vec![
("from_owned", "Entity", true),
@@ -761,7 +761,7 @@ fn incomplete_ghost_instruction(code_fragment: TokenStream, errs: Vec<(&str, &st
("ref_into", "Entity", false),
]; "7")]
#[test_case(quote! {
- #[owned_into(StuffWrapper| return StuffWrapper { payload: @ })]
+ #[owned_try_into(StuffWrapper, SomeError| return StuffWrapper { payload: @ })]
#[from_owned(StuffWrapper| return @.payload)]
struct Stuff(i32);
}, vec![]; "8")]
@@ -830,7 +830,7 @@ fn incomplete_field_attr_instruction(code_fragment: TokenStream, errs: Vec<(&str
("into_existing", "Entity", false)
]; "6")]
#[test_case(quote! {
- #[into_existing(Entity as {})]
+ #[try_into_existing(Entity as {}, SomeError)]
struct EntityDto (
#[owned_into_existing(test)]
#[ref_into_existing]
@@ -874,7 +874,7 @@ fn incomplete_field_attr_instruction_2(code_fragment: TokenStream, errs: Vec<(&s
}
}, vec!["EntityDto123"]; "into_ghosts_instr")]
#[test_case(quote! {
- #[into(EntityDto)]
+ #[try_into(EntityDto, SomeError)]
#[ghosts_ref(EntityDto123| ghost: {123})]
struct Entity123 {
test: i32
@@ -888,7 +888,7 @@ fn incomplete_field_attr_instruction_2(code_fragment: TokenStream, errs: Vec<(&s
}
}, vec!["EntityDto123"]; "into_ghosts_owned_instr")]
#[test_case(quote! {
- #[map_owned(EntityDto)]
+ #[try_map_owned(EntityDto, SomeError)]
struct Entity {
#[ghost(None)]
test: Option
@@ -930,7 +930,7 @@ fn incomplete_field_attr_instruction_2(code_fragment: TokenStream, errs: Vec<(&s
}
}, vec!["EntityDto123"]; "from_child_instr")]
#[test_case(quote! {
- #[into(EntityDto)]
+ #[try_into(EntityDto, SomeError)]
struct Entity {
#[child(EntityDto123| test)]
test: i32
@@ -971,7 +971,7 @@ fn incomplete_field_attr_instruction_2(code_fragment: TokenStream, errs: Vec<(&s
}
}, vec!["EntityDto123"]; "struct_map_map_instr")]
#[test_case(quote! {
- #[map(EnumDto)]
+ #[try_map(EnumDto, SomeError)]
enum Enum {
#[map(EnumDto123| AnotheVar)]
Variant
@@ -1022,6 +1022,112 @@ fn dedicated_field_instruction_mismatch(code_fragment: TokenStream, errs: Vec<&s
}
}
+#[test_case(quote!(try_map), None; "try_map")]
+#[test_case(quote!(try_map_owned), None; "try_map_owned")]
+#[test_case(quote!(try_map_ref), None; "try_map_ref")]
+#[test_case(quote!(try_from), None; "try_from")]
+#[test_case(quote!(try_into), None; "try_into")]
+#[test_case(quote!(try_from_owned), None; "try_from_owned")]
+#[test_case(quote!(try_from_ref), None; "try_from_ref")]
+#[test_case(quote!(owned_try_into), None; "owned_try_into")]
+#[test_case(quote!(ref_try_into), None; "ref_try_into")]
+#[test_case(quote!(try_into_existing), None; "try_into_existing")]
+#[test_case(quote!(owned_try_into_existing), None; "owned_try_into_existing")]
+#[test_case(quote!(ref_try_into_existing), None; "ref_try_into_existing")]
+
+#[test_case(quote!(try_map), Some(quote!(as {})); "try_map_as")]
+#[test_case(quote!(try_map_owned), Some(quote!(as {})); "try_map_owned_as")]
+#[test_case(quote!(try_map_ref), Some(quote!(as {})); "try_map_ref_as")]
+#[test_case(quote!(try_from), Some(quote!(as {})); "try_from_as")]
+#[test_case(quote!(try_into), Some(quote!(as {})); "try_into_as")]
+#[test_case(quote!(try_from_owned), Some(quote!(as {})); "try_from_owned_as")]
+#[test_case(quote!(try_from_ref), Some(quote!(as {})); "try_from_ref_as")]
+#[test_case(quote!(owned_try_into), Some(quote!(as {})); "owned_try_into_as")]
+#[test_case(quote!(ref_try_into), Some(quote!(as {})); "ref_try_into_as")]
+#[test_case(quote!(try_into_existing), Some(quote!(as {})); "try_into_existing_as")]
+#[test_case(quote!(owned_try_into_existing), Some(quote!(as {})); "owned_try_into_existing_as")]
+#[test_case(quote!(ref_try_into_existing), Some(quote!(as {})); "ref_try_into_existing_as")]
+
+#[test_case(quote!(try_map), Some(quote!(| return true)); "try_map_return")]
+#[test_case(quote!(try_map_owned), Some(quote!(| return true)); "try_map_owned_return")]
+#[test_case(quote!(try_map_ref), Some(quote!(| return true)); "try_map_ref_return")]
+#[test_case(quote!(try_from), Some(quote!(| return true)); "try_from_return")]
+#[test_case(quote!(try_into), Some(quote!(| return true)); "try_into_return")]
+#[test_case(quote!(try_from_owned), Some(quote!(| return true)); "try_from_owned_return")]
+#[test_case(quote!(try_from_ref), Some(quote!(| return true)); "try_from_ref_return")]
+#[test_case(quote!(owned_try_into), Some(quote!(| return true)); "owned_try_into_return")]
+#[test_case(quote!(ref_try_into), Some(quote!(| return true)); "ref_try_into_return")]
+#[test_case(quote!(try_into_existing), Some(quote!(| return true)); "try_into_existing_return")]
+#[test_case(quote!(owned_try_into_existing), Some(quote!(| return true)); "owned_try_into_existing_return")]
+#[test_case(quote!(ref_try_into_existing), Some(quote!(| return true)); "ref_try_into_existing_return")]
+fn fallible_map_instruction_no_error_type(instr: TokenStream, postfix: Option) {
+ let code_fragment = quote! {
+ #[#instr(EntityDto #postfix)]
+ struct Entity {
+ test: i32
+ }
+ };
+
+ let input: DeriveInput = syn::parse2(code_fragment).unwrap();
+ let output = derive(&input);
+ let message = get_error(output, true);
+
+ assert_eq!(message, "Error type should be specified for fallible instruction.");
+}
+
+#[test_case(quote!(map), Some(quote!(, ErrorType)); "map")]
+#[test_case(quote!(map_owned), Some(quote!(, ErrorType)); "map_owned")]
+#[test_case(quote!(map_ref), Some(quote!(, ErrorType)); "map_ref")]
+#[test_case(quote!(from), Some(quote!(, ErrorType)); "from")]
+#[test_case(quote!(into), Some(quote!(, ErrorType)); "into")]
+#[test_case(quote!(from_owned), Some(quote!(, ErrorType)); "from_owned")]
+#[test_case(quote!(from_ref), Some(quote!(, ErrorType)); "from_ref")]
+#[test_case(quote!(owned_into), Some(quote!(, ErrorType)); "owned_into")]
+#[test_case(quote!(ref_into), Some(quote!(, ErrorType)); "ref_into")]
+#[test_case(quote!(into_existing), Some(quote!(, ErrorType)); "into_existing")]
+#[test_case(quote!(owned_into_existing), Some(quote!(, ErrorType)); "owned_into_existing")]
+#[test_case(quote!(ref_into_existing), Some(quote!(, ErrorType)); "ref_into_existing")]
+
+#[test_case(quote!(map), Some(quote!(as {}, ErrorType)); "map_as")]
+#[test_case(quote!(map_owned), Some(quote!(as {}, ErrorType)); "map_owned_as")]
+#[test_case(quote!(map_ref), Some(quote!(as {}, ErrorType)); "map_ref_as")]
+#[test_case(quote!(from), Some(quote!(as {}, ErrorType)); "from_as")]
+#[test_case(quote!(into), Some(quote!(as {}, ErrorType)); "into_as")]
+#[test_case(quote!(from_owned), Some(quote!(as {}, ErrorType)); "from_owned_as")]
+#[test_case(quote!(from_ref), Some(quote!(as {}, ErrorType)); "from_ref_as")]
+#[test_case(quote!(owned_into), Some(quote!(as {}, ErrorType)); "owned_into_as")]
+#[test_case(quote!(ref_into), Some(quote!(as {}, ErrorType)); "ref_into_as")]
+#[test_case(quote!(into_existing), Some(quote!(as {}, ErrorType)); "into_existing_as")]
+#[test_case(quote!(owned_into_existing), Some(quote!(as {}, ErrorType)); "owned_into_existing_as")]
+#[test_case(quote!(ref_into_existing), Some(quote!(as {}, ErrorType)); "ref_into_existing_as")]
+
+#[test_case(quote!(map), Some(quote!(, ErrorType| return true)); "map_return")]
+#[test_case(quote!(map_owned), Some(quote!(, ErrorType| return true)); "map_owned_return")]
+#[test_case(quote!(map_ref), Some(quote!(, ErrorType| return true)); "map_ref_return")]
+#[test_case(quote!(from), Some(quote!(, ErrorType| return true)); "from_return")]
+#[test_case(quote!(into), Some(quote!(, ErrorType| return true)); "into_return")]
+#[test_case(quote!(from_owned), Some(quote!(, ErrorType| return true)); "from_owned_return")]
+#[test_case(quote!(from_ref), Some(quote!(, ErrorType| return true)); "from_ref_return")]
+#[test_case(quote!(owned_into), Some(quote!(, ErrorType| return true)); "owned_into_return")]
+#[test_case(quote!(ref_into), Some(quote!(, ErrorType| return true)); "ref_into_return")]
+#[test_case(quote!(into_existing), Some(quote!(, ErrorType| return true)); "into_existing_return")]
+#[test_case(quote!(owned_into_existing), Some(quote!(, ErrorType| return true)); "owned_into_existing_return")]
+#[test_case(quote!(ref_into_existing), Some(quote!(, ErrorType| return true)); "ref_into_existing_return")]
+fn infallible_map_instruction_error_type(instr: TokenStream, postfix: Option) {
+ let code_fragment = quote! {
+ #[#instr(EntityDto #postfix)]
+ struct Entity {
+ test: i32
+ }
+ };
+
+ let input: DeriveInput = syn::parse2(code_fragment).unwrap();
+ let output = derive(&input);
+ let message = get_error(output, true);
+
+ assert_eq!(message, "Error type should not be specified for infallible instruction.");
+}
+
fn get_error(output: Result, expect_root_error: bool) -> String {
assert!(output.is_err());
let mut err_iter = output.unwrap_err().into_iter();
diff --git a/o2o-impl/src/validate.rs b/o2o-impl/src/validate.rs
index 53d43b4..375537c 100644
--- a/o2o-impl/src/validate.rs
+++ b/o2o-impl/src/validate.rs
@@ -14,12 +14,19 @@ pub(crate) fn validate(input: &DataType) -> Result<()> {
validate_error_instrs(input, attrs, &mut errors);
- validate_struct_attrs(attrs.iter_for_kind(&Kind::FromOwned), &mut errors);
- validate_struct_attrs(attrs.iter_for_kind(&Kind::FromRef), &mut errors);
- validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedInto), &mut errors);
- validate_struct_attrs(attrs.iter_for_kind(&Kind::RefInto), &mut errors);
- validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedIntoExisting), &mut errors);
- validate_struct_attrs(attrs.iter_for_kind(&Kind::RefIntoExisting), &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::FromOwned, false), false, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::FromRef, false), false, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedInto, false), false, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::RefInto, false), false, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedIntoExisting, false), false, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::RefIntoExisting, false), false, &mut errors);
+
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::FromOwned, true), true, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::FromRef, true), true, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedInto, true), true, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::RefInto, true), true, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedIntoExisting, true), true, &mut errors);
+ validate_struct_attrs(attrs.iter_for_kind(&Kind::RefIntoExisting, true), true, &mut errors);
let type_paths = attrs.attrs.iter()
.map(|x| &x.attr.ty)
@@ -94,12 +101,20 @@ fn validate_member_error_instrs(input: &DataType, attrs: &MemberAttrs, errors: &
}
}
-fn validate_struct_attrs<'a, I: Iterator
- >(attrs: I, errors: &mut HashMap) {
+fn validate_struct_attrs<'a, I: Iterator
- >(attrs: I, fallible: bool, errors: &mut HashMap) {
let mut unique_ident = HashSet::new();
for attr in attrs {
if !unique_ident.insert(&attr.ty) {
errors.insert("Ident here must be unique.".into(), attr.ty.span);
}
+
+ if fallible && attr.err_ty.is_none() {
+ errors.insert("Error type should be specified for fallible instruction.".into(), attr.ty.span);
+ }
+
+ if !fallible && attr.err_ty.is_some() {
+ errors.insert("Error type should not be specified for infallible instruction.".into(), attr.err_ty.as_ref().unwrap().span);
+ }
}
}
@@ -178,13 +193,17 @@ fn validate_dedicated_member_attrs Option<&TypePath>>(attrs: &Ve
}
fn validate_fields(input: &Struct, struct_attrs: &DataTypeAttrs, type_paths: &HashSet<&TypePath>, errors: &mut HashMap) {
- let into_type_paths = struct_attrs.iter_for_kind(&Kind::OwnedInto)
- .chain(struct_attrs.iter_for_kind(&Kind::RefInto))
+ let into_type_paths = struct_attrs.iter_for_kind(&Kind::OwnedInto, false)
+ .chain(struct_attrs.iter_for_kind(&Kind::RefInto, false))
+ .chain(struct_attrs.iter_for_kind(&Kind::OwnedInto, true))
+ .chain(struct_attrs.iter_for_kind(&Kind::RefInto, true))
.map(|x| &x.ty)
.collect::>();
- let from_type_paths = struct_attrs.iter_for_kind(&Kind::FromOwned)
- .chain(struct_attrs.iter_for_kind(&Kind::FromRef))
+ let from_type_paths = struct_attrs.iter_for_kind(&Kind::FromOwned, false)
+ .chain(struct_attrs.iter_for_kind(&Kind::FromRef, false))
+ .chain(struct_attrs.iter_for_kind(&Kind::FromOwned, true))
+ .chain(struct_attrs.iter_for_kind(&Kind::FromRef, true))
.filter(|x| x.update.is_none())
.map(|x| &x.ty)
.collect::>();
@@ -227,12 +246,19 @@ fn validate_fields(input: &Struct, struct_attrs: &DataTypeAttrs, type_paths: &Ha
}
if !input.named_fields {
- let struct_attrs = struct_attrs.iter_for_kind(&Kind::OwnedInto).map(|x| (x, Kind::OwnedInto))
- .chain(struct_attrs.iter_for_kind(&Kind::RefInto).map(|x| (x, Kind::RefInto)))
- .chain(struct_attrs.iter_for_kind(&Kind::OwnedIntoExisting).map(|x| (x, Kind::OwnedIntoExisting)))
- .chain(struct_attrs.iter_for_kind(&Kind::RefIntoExisting).map(|x| (x, Kind::RefIntoExisting)))
- .chain(struct_attrs.iter_for_kind(&Kind::FromOwned).map(|x| (x, Kind::FromOwned)))
- .chain(struct_attrs.iter_for_kind(&Kind::FromRef).map(|x| (x, Kind::FromRef)));
+ let struct_attrs: Vec<(&TraitAttrCore, Kind)> = struct_attrs.iter_for_kind(&Kind::OwnedInto, false).map(|x| (x, Kind::OwnedInto))
+ .chain(struct_attrs.iter_for_kind(&Kind::RefInto, false).map(|x| (x, Kind::RefInto)))
+ .chain(struct_attrs.iter_for_kind(&Kind::OwnedIntoExisting, false).map(|x| (x, Kind::OwnedIntoExisting)))
+ .chain(struct_attrs.iter_for_kind(&Kind::RefIntoExisting, false).map(|x| (x, Kind::RefIntoExisting)))
+ .chain(struct_attrs.iter_for_kind(&Kind::FromOwned, false).map(|x| (x, Kind::FromOwned)))
+ .chain(struct_attrs.iter_for_kind(&Kind::FromRef, false).map(|x| (x, Kind::FromRef)))
+ .chain(struct_attrs.iter_for_kind(&Kind::RefInto, true).map(|x| (x, Kind::OwnedInto)))
+ .chain(struct_attrs.iter_for_kind(&Kind::RefInto, true).map(|x| (x, Kind::RefInto)))
+ .chain(struct_attrs.iter_for_kind(&Kind::OwnedIntoExisting, true).map(|x| (x, Kind::OwnedIntoExisting)))
+ .chain(struct_attrs.iter_for_kind(&Kind::RefIntoExisting, true).map(|x| (x, Kind::RefIntoExisting)))
+ .chain(struct_attrs.iter_for_kind(&Kind::FromOwned, true).map(|x| (x, Kind::FromOwned)))
+ .chain(struct_attrs.iter_for_kind(&Kind::FromRef, true).map(|x| (x, Kind::FromRef)))
+ .collect();
for (struct_attr, kind) in struct_attrs {
if struct_attr.quick_return.is_none() && struct_attr.type_hint == TypeHint::Struct {
@@ -241,7 +267,7 @@ fn validate_fields(input: &Struct, struct_attrs: &DataTypeAttrs, type_paths: &Ha
continue;
}
- if let Some(field_attr) = field.attrs.applicable_field_attr(&kind, &struct_attr.ty) {
+ if let Some(field_attr) = field.attrs.applicable_field_attr(&kind, false, &struct_attr.ty) {
if kind == Kind::FromOwned || kind == Kind::FromRef {
if field_attr.attr.member.is_none() && field_attr.attr.action.is_none() {
errors.insert(format!("Member trait instruction #[{}(...)] for member {} should specify corresponding field name of the {} or an action", field_attr.original_instr, field.member.to_token_stream(), struct_attr.ty.path), field.member.span());
diff --git a/o2o-macros/src/lib.rs b/o2o-macros/src/lib.rs
index e79b529..0c9d696 100644
--- a/o2o-macros/src/lib.rs
+++ b/o2o-macros/src/lib.rs
@@ -5,7 +5,15 @@ use o2o_impl::expand::derive;
use syn::{parse_macro_input, DeriveInput};
/// ### Object to Object mapper for Rust
-/// **o2o** can implement `std::convert::From`, `std::convert::Into`, and custom `o2o::traits::IntoExisting` traits via procedural macro.
+/// **o2o** can implement ...
+/// * `std::convert::From`
+/// * `std::convert::TryFrom`
+/// * `std::convert::Into`
+/// * `std::convert::TryInto`
+/// * `o2o::traits::IntoExisting`
+/// * `o2o::traits::TryIntoExisting`
+///
+/// ... traits via procedural macro.
///
/// ### Basic example
///
@@ -20,7 +28,7 @@ use syn::{parse_macro_input, DeriveInput};
///
/// #[derive(o2o)]
/// #[from_owned(Person)] // This tells o2o to generate 'From for PersonDto' implementation
-/// #[owned_into(Person)] // This generates 'Into for PersonDto'
+/// #[owned_try_into(Person, std::io::Error)] // This generates 'TryInto for PersonDto' with type Error = std::io::Error
/// struct PersonDto {
/// id: u32,
/// name: String,
@@ -37,19 +45,48 @@ use syn::{parse_macro_input, DeriveInput};
/// // and this:
///
/// let dto = PersonDto { id: 321, name: "Jack".into(), age: 23 };
-/// let person: Person = dto.into();
+/// let person: Person = dto.try_into().unwrap();
///
/// assert_eq!(person.id, 321); assert_eq!(person.name, "Jack"); assert_eq!(person.age, 23);
+///
+/// // o2o also supports enums:
+///
+/// enum Creature {
+/// Person(Person),
+/// Cat { nickname: String },
+/// Dog(String),
+/// Other
+/// }
+///
+/// #[derive(o2o)]
+/// #[from_owned(Creature)]
+/// enum CreatureDto {
+/// Person(#[from(~.into())] PersonDto),
+/// Cat { nickname: String },
+/// Dog(String),
+/// Other
+/// }
+///
+/// let creature = Creature::Cat { nickname: "Floppa".into() };
+/// let dto: CreatureDto = creature.into();
+///
+/// if let CreatureDto::Cat { nickname } = dto { assert_eq!(nickname, "Floppa"); } else { assert!(false) }
/// ```
///
-/// For more examples, visit [github.com](https://github.com/Artem-Romanenia/o2o)
+/// ### For more examples, visit [github.com](https://github.com/Artem-Romanenia/o2o)
#[proc_macro_derive(o2o, attributes(
owned_into, ref_into, into,
from_owned, from_ref, from,
- map_owned, map_ref, map,
+ map_owned, map_ref, map,
+ owned_try_into, ref_try_into, try_into,
owned_into_existing, ref_into_existing, into_existing,
+ try_from_owned, try_from_ref, try_from,
+ try_map_owned, try_map_ref, try_map,
+ owned_try_into_existing, ref_try_into_existing, try_into_existing,
child, children, parent, ghost, ghosts, where_clause,
literal, pattern, o2o))]
+ // TODO: Research, are there any downsides of having that many attributes?
+ // (given that all but one are essentially shortcuts and can be avoided with alternative instr syntax)
pub fn derive_o2o(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive(&input)
diff --git a/o2o-tests/Cargo.toml b/o2o-tests/Cargo.toml
index c21455b..427ea3c 100644
--- a/o2o-tests/Cargo.toml
+++ b/o2o-tests/Cargo.toml
@@ -9,6 +9,7 @@ repository = "https://github.com/Artem-Romanenia/o2o"
[dependencies]
o2o = { version = "0.4.3", path = "../" }
+anyhow = "1.0.86"
[dev-dependencies]
test-case = "3"
\ No newline at end of file
diff --git a/o2o-tests/tests/10_multiple_child_attr_tests_fallible.rs b/o2o-tests/tests/10_multiple_child_attr_tests_fallible.rs
new file mode 100644
index 0000000..123b5ee
--- /dev/null
+++ b/o2o-tests/tests/10_multiple_child_attr_tests_fallible.rs
@@ -0,0 +1,616 @@
+use o2o::{o2o, traits::TryIntoExisting};
+
+#[derive(Default)]
+struct Entity {
+ parent_int: i32,
+ base: BaseEntity,
+ child: Child,
+}
+
+#[derive(Default)]
+struct TupleEntity(i32, TupleBaseEntity, TupleChild);
+
+#[derive(Default)]
+struct BaseEntity {
+ base: Base,
+ base_entity_int: i32,
+}
+
+#[derive(Default)]
+struct TupleBaseEntity(TupleBase, i32);
+
+#[derive(Default)]
+struct Base {
+ base_int_2: i32,
+ another_base_int: i32,
+}
+
+#[derive(Default)]
+struct TupleBase(i32, i32);
+
+#[derive(Default)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(Default)]
+struct TupleChild(i32, i32);
+
+#[derive(o2o)]
+#[o2o(
+ try_map(Entity, String),
+ try_map(TupleEntity as (), String),
+ try_into_existing(Entity, String),
+ try_into_existing(TupleEntity as (), String),
+ children(Entity| base: BaseEntity, base.base: Base, child: Child),
+ children(TupleEntity| 1: TupleBaseEntity as (), 1 .0: TupleBase as (), 2: TupleChild as ()),
+)]
+struct EntityDto {
+ #[map(TupleEntity| 0)]
+ parent_int: i32,
+ #[o2o(
+ child(Entity| base.base),
+ child(TupleEntity| 1 .0),
+ map(Entity| base_int_2),
+ map(TupleEntity| 0),
+ )]
+ base_int: i32,
+ #[child(Entity| base.base)]
+ #[child(TupleEntity| 1 .0)]
+ #[map(TupleEntity| 1)]
+ another_base_int: i32,
+ #[child(Entity| base)]
+ #[child(TupleEntity| 1)]
+ #[map(TupleEntity| 1)]
+ base_entity_int: i32,
+ #[child(Entity| child)]
+ #[child(TupleEntity| 2)]
+ #[map(TupleEntity| 0)]
+ child_int: i32,
+ #[child(Entity| child)]
+ #[child(TupleEntity| 2)]
+ #[map(TupleEntity| 1)]
+ another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(Entity as {}, String)]
+#[try_map(TupleEntity, String)]
+#[try_into_existing(Entity as {}, String)]
+#[try_into_existing(TupleEntity, String)]
+#[children(Entity| base: BaseEntity as {}, base.base: Base as {}, child: Child as {})]
+#[children(TupleEntity| 1: TupleBaseEntity, 1 .0: TupleBase, 2: TupleChild)]
+struct TupleEntityDto (
+ #[map(Entity| parent_int)]
+ i32,
+ #[o2o(child(Entity| base.base))]
+ #[o2o(child(TupleEntity| 1 .0))]
+ #[o2o(map(Entity| base_int_2))]
+ #[o2o(map(TupleEntity| 0))]
+ i32,
+ #[child(Entity| base.base)]
+ #[child(TupleEntity| 1 .0)]
+ #[map(Entity| another_base_int)]
+ #[map(TupleEntity| 1)]
+ i32,
+ #[child(Entity| base)]
+ #[child(TupleEntity| 1)]
+ #[map(Entity| base_entity_int)]
+ #[map(TupleEntity| 1)]
+ i32,
+ #[child(Entity| child)]
+ #[child(TupleEntity| 2)]
+ #[map(Entity| child_int)]
+ #[map(TupleEntity| 0)]
+ i32,
+ #[child(Entity| child)]
+ #[child(TupleEntity| 2)]
+ #[map(Entity| another_child_int)]
+ #[map(TupleEntity| 1)]
+ i32,
+);
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn named2unnamed() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn named2unnamed_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.base.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.base.another_base_int);
+ assert_eq!(dto.base_entity_int, entity.base.base_entity_int);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn named2unnamed_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.0);
+ assert_eq!(dto.base_int, entity.1.0.0);
+ assert_eq!(dto.another_base_int, entity.1.0.1);
+ assert_eq!(dto.base_entity_int, entity.1.1);
+ assert_eq!(dto.child_int, entity.2.0);
+ assert_eq!(dto.another_child_int, entity.2.1);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.base.base_int_2, dto.base_int);
+ assert_eq!(entity.base.base.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.base_entity_int, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn named2unnamed_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.0);
+ assert_eq!(entity.base.base.base_int_2, dto.1);
+ assert_eq!(entity.base.base.another_base_int, dto.2);
+ assert_eq!(entity.base.base_entity_int, dto.3);
+ assert_eq!(entity.child.child_int, dto.4);
+ assert_eq!(entity.child.another_child_int, dto.5);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn unnamed2named() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn unnamed2named_reverse() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.0.0);
+ assert_eq!(dto.2, entity.1.0.1);
+ assert_eq!(dto.3, entity.1.1);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
+
+#[test]
+fn unnamed2named_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.parent_int);
+ assert_eq!(dto.1, entity.base.base.base_int_2);
+ assert_eq!(dto.2, entity.base.base.another_base_int);
+ assert_eq!(dto.3, entity.base.base_entity_int);
+ assert_eq!(dto.4, entity.child.child_int);
+ assert_eq!(dto.5, entity.child.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.0.0, dto.1);
+ assert_eq!(entity.1.0.1, dto.2);
+ assert_eq!(entity.1.1, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
+
+#[test]
+fn unnamed2named_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.parent_int);
+ assert_eq!(entity.1.0.0, dto.base_int);
+ assert_eq!(entity.1.0.1, dto.another_base_int);
+ assert_eq!(entity.1.1, dto.base_entity_int);
+ assert_eq!(entity.2.0, dto.child_int);
+ assert_eq!(entity.2.1, dto.another_child_int);
+}
+
+#[test]
+fn existing_named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_named2unnamed() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.base.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.base.another_base_int);
+ assert_eq!(dto.base_entity_int, entity.base.base_entity_int);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_named2unnamed_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.parent_int, entity.0);
+ assert_eq!(dto.base_int, entity.1.0.0);
+ assert_eq!(dto.another_base_int, entity.1.0.1);
+ assert_eq!(dto.base_entity_int, entity.1.1);
+ assert_eq!(dto.child_int, entity.2.0);
+ assert_eq!(dto.another_child_int, entity.2.1);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn existing_unnamed2named() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.0.0);
+ assert_eq!(dto.2, entity.1.0.1);
+ assert_eq!(dto.3, entity.1.1);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
+
+#[test]
+fn existing_unnamed2named_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.0, entity.parent_int);
+ assert_eq!(dto.1, entity.base.base.base_int_2);
+ assert_eq!(dto.2, entity.base.base.another_base_int);
+ assert_eq!(dto.3, entity.base.base_entity_int);
+ assert_eq!(dto.4, entity.child.child_int);
+ assert_eq!(dto.5, entity.child.another_child_int);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/11_child_attr_mixed_struct_kinds_tests_fallible.rs b/o2o-tests/tests/11_child_attr_mixed_struct_kinds_tests_fallible.rs
new file mode 100644
index 0000000..1b15722
--- /dev/null
+++ b/o2o-tests/tests/11_child_attr_mixed_struct_kinds_tests_fallible.rs
@@ -0,0 +1,319 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Entity {
+ parent_int: i32,
+ base: TupleBaseEntity,
+ child: Child,
+}
+
+#[derive(Default)]
+struct TupleEntity(i32, BaseEntity, TupleChild);
+
+#[derive(Default)]
+struct BaseEntity {
+ base: TupleBase,
+ base_entity_int: i32,
+}
+
+#[derive(Default)]
+struct TupleBaseEntity(Base, i32);
+
+#[derive(Default)]
+struct Base {
+ base_int_2: i32,
+ another_base_int: i32,
+}
+
+#[derive(Default)]
+struct TupleBase(i32, i16);
+
+#[derive(Default)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(Default)]
+struct TupleChild(i32, i16);
+
+#[derive(o2o)]
+#[o2o(
+ try_map(Entity, String),
+ try_into_existing(Entity, String),
+ children(Entity| base: TupleBaseEntity as (), base.0: Base, child: Child),
+)]
+struct EntityDto {
+ parent_int: i32,
+ #[o2o(child(base.0), map(base_int_2))] base_int: i32,
+ #[child(base.0)] another_base_int: i32,
+ #[child(base)] #[map(1)] base_entity_int: i32,
+ #[child(child)] child_int: i32,
+ #[child(child)] another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(TupleEntity, String)]
+#[try_into_existing(TupleEntity, String)]
+#[children(TupleEntity| 1: BaseEntity as {}, 1.base: TupleBase, 2: TupleChild)]
+struct TupleEntityDto (
+ i32,
+ #[child(1.base)] #[map(0)] i32,
+ #[child(1.base)] #[map(1)] i16,
+ #[child(1)] #[map(base_entity_int)] i32,
+ #[child(2)] #[map(0)] i32,
+ #[child(2)] #[map(1)] i16,
+);
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.0.base_int_2);
+ assert_eq!(456, entity.base.0.another_base_int);
+ assert_eq!(654, entity.base.1);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: TupleBaseEntity (
+ Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ 654
+ ),
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.0.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.0.another_base_int);
+ assert_eq!(dto.base_entity_int, entity.base.1);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: TupleBaseEntity (
+ Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ 654
+ ),
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.0.base_int_2, dto.base_int);
+ assert_eq!(entity.base.0.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.1, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.base.0);
+ assert_eq!(456, entity.1.base.1);
+ assert_eq!(654, entity.1.base_entity_int);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let entity = TupleEntity(
+ 123,
+ BaseEntity {
+ base: TupleBase (
+ 321,
+ 456
+ ),
+ base_entity_int: 654
+ },
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.base.0);
+ assert_eq!(dto.2, entity.1.base.1);
+ assert_eq!(dto.3, entity.1.base_entity_int);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ BaseEntity {
+ base: TupleBase (
+ 321,
+ 456
+ ),
+ base_entity_int: 654
+ },
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.base.0, dto.1);
+ assert_eq!(entity.1.base.1, dto.2);
+ assert_eq!(entity.1.base_entity_int, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
+
+#[test]
+fn existing_named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.0.base_int_2);
+ assert_eq!(456, entity.base.0.another_base_int);
+ assert_eq!(654, entity.base.1);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.0.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.0.another_base_int);
+ assert_eq!(dto.base_entity_int, entity.base.1);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.base.0);
+ assert_eq!(456, entity.1.base.1);
+ assert_eq!(654, entity.1.base_entity_int);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.base.0);
+ assert_eq!(dto.2, entity.1.base.1);
+ assert_eq!(dto.3, entity.1.base_entity_int);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/12_ghost_attr_props_fallible.rs b/o2o-tests/tests/12_ghost_attr_props_fallible.rs
new file mode 100644
index 0000000..591cf7c
--- /dev/null
+++ b/o2o-tests/tests/12_ghost_attr_props_fallible.rs
@@ -0,0 +1,522 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct EntityModel {
+ some_int: i32,
+ another_int: i32,
+ ghost_int: i32,
+ ghost_int_2: i16,
+ ghost_float: f32,
+}
+
+#[derive(Default)]
+struct TupleEntityModel(i32, i16, i32, i16, f32);
+
+#[derive(Default)]
+#[derive(o2o)]
+#[try_map(EntityModel, String)]
+#[try_into_existing(EntityModel, String)]
+#[ghosts(ghost_int: { @.some_int }, ghost_int_2: { @.another_int as i16 }, ghost_float: { 456.0 })]
+struct Entity {
+ some_int: i32,
+ another_int: i32,
+}
+
+#[derive(o2o)]
+#[try_into(Entity, String)]
+#[try_into_existing(Entity, String)]
+struct Entity2 {
+ some_int: i32,
+ another_int: i32,
+ #[ghost()]
+ _some_float: f32
+}
+
+#[derive(Default)]
+#[derive(o2o)]
+#[try_map(TupleEntityModel, String)]
+#[try_into_existing(TupleEntityModel, String)]
+#[o2o(ghosts_owned(2: { @.0 }, 3: { @.1 as i16 }, 4: { 456.0 }))]
+#[o2o(ghosts_ref(2: { @.0 }, 3: { @.1 as i16 }, 4: { 4567.0 }))]
+struct TupleEntity (i32, i16);
+
+#[derive(o2o)]
+#[try_map(Entity, String)]
+#[try_into_existing(Entity, String)]
+struct EntityDto {
+ some_int: i32,
+ another_int: i32,
+ #[o2o(ghost(@.some_int))]
+ ghost_int: i32,
+ #[o2o(ghost(@.another_int as i16))]
+ ghost_int_2: i16,
+ #[o2o(ghost_owned({456.0}))]
+ #[o2o(ghost_ref({4567.0}))]
+ ghost_float: f32,
+}
+
+#[derive(o2o)]
+#[try_map(TupleEntity, String)]
+#[try_into_existing(TupleEntity, String)]
+struct TupleEntityDto(
+ i32,
+ i16,
+ #[ghost(@.0)]
+ i32,
+ #[ghost(@.1 as i16)]
+ i16,
+ #[o2o(ghost_owned({456.0}))]
+ #[o2o(ghost_ref(4567.0))]
+ f32
+);
+
+#[test]
+fn named2named_basic() {
+ let named2 = Entity2 {
+ some_int: 123,
+ another_int: 321,
+ _some_float: 456.0
+ };
+
+ let named: Entity = named2.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn named2named_ref_basic() {
+ let named2 = &Entity2 {
+ some_int: 123,
+ another_int: 321,
+ _some_float: 456.0
+ };
+
+ let named: Entity = named2.try_into().unwrap();
+
+ assert_eq!(named2.some_int, named.some_int);
+ assert_eq!(named2.another_int, named.another_int);
+}
+
+#[test]
+fn named2named() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: EntityDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.another_int);
+ assert_eq!(123, dto.ghost_int);
+ assert_eq!(321, dto.ghost_int_2);
+ assert_eq!(456.0, dto.ghost_float);
+}
+
+#[test]
+fn named2named_2() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let model: EntityModel = named.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(123, model.ghost_int);
+ assert_eq!(321, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn named2named_reverse() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn named2named_reverse_2() {
+ let model = EntityModel {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = model.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn named2named_ref() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: EntityDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.another_int);
+ assert_eq!(named.some_int, dto.ghost_int);
+ assert_eq!(named.another_int as i16, dto.ghost_int_2);
+ assert_eq!(4567.0, dto.ghost_float);
+}
+
+#[test]
+fn named2named_ref_2() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let model: EntityModel = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, model.some_int);
+ assert_eq!(named.another_int, model.another_int);
+ assert_eq!(named.some_int, model.ghost_int);
+ assert_eq!(named.another_int as i16, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int as i32, named.another_int);
+}
+
+#[test]
+fn named2named_reverse_ref_2() {
+ let model = &EntityModel {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, named.some_int);
+ assert_eq!(model.another_int as i32, named.another_int);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(123, dto.2);
+ assert_eq!(321, dto.3);
+ assert_eq!(456.0, dto.4);
+}
+
+#[test]
+fn unnamed2unnamed_2() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let model: TupleEntityModel = entity.try_into().unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+ assert_eq!(123, model.2);
+ assert_eq!(321, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let dto = TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_2() {
+ let model = TupleEntityModel (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = model.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1, dto.1);
+ assert_eq!(entity.0, dto.2);
+ assert_eq!(entity.1 as i16, dto.3);
+ assert_eq!(4567.0, dto.4);
+}
+
+#[test]
+fn unnamed2unnamed_ref_2() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let model: TupleEntityModel = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, model.0);
+ assert_eq!(entity.1, model.1);
+ assert_eq!(entity.0, model.2);
+ assert_eq!(entity.1 as i16, model.3);
+ assert_eq!(4567.0, model.4);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let dto = &TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref_2() {
+ let model = &TupleEntityModel (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = model.try_into().unwrap();
+
+ assert_eq!(model.0, entity.0);
+ assert_eq!(model.1, entity.1);
+}
+
+#[test]
+fn existing_named2named_basic() {
+ let named2 = Entity2 {
+ some_int: 123,
+ another_int: 321,
+ _some_float: 456.0
+ };
+
+ let mut named: Entity = Default::default();
+ named2.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn existing_named2named_ref_basic() {
+ let named2 = &Entity2 {
+ some_int: 123,
+ another_int: 321,
+ _some_float: 456.0
+ };
+
+ let mut named: Entity = Default::default();
+ named2.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(named2.some_int, named.some_int);
+ assert_eq!(named2.another_int, named.another_int);
+}
+
+#[test]
+fn existing_named2named() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: EntityModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(123, model.ghost_int);
+ assert_eq!(321, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: EntityModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(named.some_int, model.some_int);
+ assert_eq!(named.another_int, model.another_int);
+ assert_eq!(named.some_int, model.ghost_int);
+ assert_eq!(named.another_int as i16, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn existing_named2named_reverse() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let mut named: Entity = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn existing_named2named_reverse_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let mut named: Entity = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int as i32, named.another_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let named = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let mut model: TupleEntityModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+ assert_eq!(123, model.2);
+ assert_eq!(321, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let mut model: TupleEntityModel = Default::default();
+ entity.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(entity.0, model.0);
+ assert_eq!(entity.1, model.1);
+ assert_eq!(entity.0, model.2);
+ assert_eq!(entity.1 as i16, model.3);
+ assert_eq!(4567.0, model.4);
+}
+
+#[test]
+fn existing_unnamed2unnamed_reverse() {
+ let dto = TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn existing_unnamed2unnamed_reverse_ref() {
+ let dto = &TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/13_multiple_ghost_attr_props_fallible.rs b/o2o-tests/tests/13_multiple_ghost_attr_props_fallible.rs
new file mode 100644
index 0000000..8aefaca
--- /dev/null
+++ b/o2o-tests/tests/13_multiple_ghost_attr_props_fallible.rs
@@ -0,0 +1,897 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct EntityModel {
+ some_int: i32,
+ another_int: i32,
+ ghost_int: i32,
+ ghost_int_2: i16,
+ ghost_float: f32,
+}
+
+#[derive(Default)]
+struct TupleEntityModel(i32, i16, i32, i16, f32);
+
+#[derive(Default)]
+#[derive(o2o)]
+#[o2o(
+ try_map(EntityModel, String),
+ try_into_existing(EntityModel, String),
+ ghosts_owned(EntityModel|
+ ghost_int: { @.some_int },
+ ghost_int_2: { @.another_int as i16 },
+ ghost_float: { 456.0 }
+ ),
+ ghosts_ref(EntityModel|
+ ghost_int: { @.some_int },
+ ghost_int_2: { @.another_int as i16 },
+ ghost_float: { 4567.0 }
+ ),
+ try_map(TupleEntityModel as (), String),
+ try_into_existing(TupleEntityModel as (), String),
+ ghosts(TupleEntityModel|
+ 2: { @.some_int },
+ 3: { @.another_int as i16 },
+ 4: { 456.0 }
+ )
+)]
+struct Entity {
+ some_int: i32,
+ #[from(TupleEntityModel| @.1 as i32)]
+ #[into(TupleEntityModel| ~ as i16)]
+ another_int: i32,
+}
+
+#[derive(Default)]
+#[derive(o2o)]
+#[try_map(TupleEntityModel, String)]
+#[try_into_existing(TupleEntityModel, String)]
+#[ghosts(TupleEntityModel|
+ 2: { @.0 },
+ 3: { @.1 as i16 },
+ 4: { 456.0 }
+)]
+#[try_map(EntityModel as {}, String)]
+#[try_into_existing(EntityModel as {}, String)]
+#[ghosts(EntityModel|
+ ghost_int: { @.0 },
+ ghost_int_2: { @.1 as i16 },
+ ghost_float: { 456.0 }
+)]
+struct TupleEntity (
+ #[map(EntityModel| some_int)]
+ i32,
+ #[into(EntityModel| another_int, ~ as i32)]
+ #[from(EntityModel| @.another_int as i16)]
+ i16
+);
+
+#[derive(o2o)]
+#[try_map(Entity, String)]
+#[try_map(TupleEntity as (), String)]
+#[try_into_existing(Entity, String)]
+#[try_into_existing(TupleEntity as (), String)]
+struct EntityDto {
+ #[map(TupleEntity| 0)]
+ some_int: i32,
+ #[into(TupleEntity| ~ as i16)]
+ #[from(TupleEntity| @.1 as i32)]
+ another_int: i32,
+ #[ghost(Entity| @.some_int)]
+ #[ghost(TupleEntity| @.0)]
+ ghost_int: i32,
+ #[ghost(Entity| @.another_int as i16)]
+ #[ghost(TupleEntity| @.1)]
+ ghost_int_2: i16,
+ #[ghost({456.0})]
+ ghost_float: f32,
+}
+
+#[derive(o2o)]
+#[try_map(TupleEntity, String)]
+#[try_map(Entity as {}, String)]
+#[try_into_existing(TupleEntity, String)]
+#[try_into_existing(Entity as {}, String)]
+struct TupleEntityDto(
+ #[map(Entity| some_int)]
+ i32,
+ #[into(Entity| another_int, @.1 as i32)]
+ #[from(Entity| @.another_int as i16)]
+ i16,
+ #[o2o(
+ ghost(TupleEntity| @.0),
+ ghost(Entity| @.some_int),
+ )]
+ i32,
+ #[ghost(TupleEntity| @.1)]
+ #[ghost(Entity| @.another_int as i16)]
+ i16,
+ #[o2o(ghost_owned(456.0))]
+ #[o2o(ghost_ref({4567.0}))]
+ f32
+);
+
+#[test]
+fn named2named() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: EntityDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.another_int);
+ assert_eq!(123, dto.ghost_int);
+ assert_eq!(321, dto.ghost_int_2);
+ assert_eq!(456.0, dto.ghost_float);
+}
+
+#[test]
+fn named2named_2() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let model: EntityModel = named.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(123, model.ghost_int);
+ assert_eq!(321, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn named2named_reverse() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn named2named_reverse_2() {
+ let model = EntityModel {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = model.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn named2named_ref() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: EntityDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.another_int);
+ assert_eq!(named.some_int, dto.ghost_int);
+ assert_eq!(named.another_int as i16, dto.ghost_int_2);
+ assert_eq!(456.0, dto.ghost_float);
+}
+
+#[test]
+fn named2named_ref_2() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let model: EntityModel = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, model.some_int);
+ assert_eq!(named.another_int, model.another_int);
+ assert_eq!(named.some_int, model.ghost_int);
+ assert_eq!(named.another_int as i16, model.ghost_int_2);
+ assert_eq!(4567.0, model.ghost_float);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int as i32, named.another_int);
+}
+
+#[test]
+fn named2named_reverse_ref_2() {
+ let model = &EntityModel {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let named: Entity = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, named.some_int);
+ assert_eq!(model.another_int as i32, named.another_int);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(123, dto.2);
+ assert_eq!(321, dto.3);
+ assert_eq!(456.0, dto.4);
+}
+
+#[test]
+fn unnamed2unnamed_2() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let model: TupleEntityModel = entity.try_into().unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+ assert_eq!(123, model.2);
+ assert_eq!(321, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let dto = TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_2() {
+ let model = TupleEntityModel (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = model.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1, dto.1);
+ assert_eq!(entity.0, dto.2);
+ assert_eq!(entity.1 as i16, dto.3);
+ assert_eq!(4567.0, dto.4);
+}
+
+#[test]
+fn unnamed2unnamed_ref_2() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let model: TupleEntityModel = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, model.0);
+ assert_eq!(entity.1, model.1);
+ assert_eq!(entity.0, model.2);
+ assert_eq!(entity.1 as i16, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let dto = &TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref_2() {
+ let model = &TupleEntityModel (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: TupleEntity = model.try_into().unwrap();
+
+ assert_eq!(model.0, entity.0);
+ assert_eq!(model.1, entity.1);
+}
+
+#[test]
+fn named2unnamed() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: TupleEntityDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(123, dto.2);
+ assert_eq!(321, dto.3);
+ assert_eq!(456.0, dto.4);
+}
+
+#[test]
+fn named2unnamed_2() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let model: TupleEntityModel = named.try_into().unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+ assert_eq!(123, model.2);
+ assert_eq!(321, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn named2unnamed_reverse() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn named2unnamed_reverse_2() {
+ let model = EntityModel {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let entity: TupleEntity = model.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn named2unnamed_ref() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: TupleEntityDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.0);
+ assert_eq!(named.another_int as i16, dto.1);
+ assert_eq!(named.some_int, dto.2);
+ assert_eq!(named.another_int as i16, dto.3);
+ assert_eq!(4567.0, dto.4);
+}
+
+#[test]
+fn named2unnamed_ref_2() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let model: TupleEntityModel = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, model.0);
+ assert_eq!(named.another_int as i16, model.1);
+ assert_eq!(named.some_int, model.2);
+ assert_eq!(named.another_int as i16, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn named2unnamed_reverse_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, entity.0);
+ assert_eq!(dto.another_int as i16, entity.1);
+}
+
+#[test]
+fn named2unnamed_reverse_ref_2() {
+ let model = &EntityModel {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let entity: TupleEntity = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, entity.0);
+ assert_eq!(model.another_int as i16, entity.1);
+}
+
+#[test]
+fn unnamed2named() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.another_int);
+ assert_eq!(123, dto.ghost_int);
+ assert_eq!(321, dto.ghost_int_2);
+ assert_eq!(456.0, dto.ghost_float);
+}
+
+#[test]
+fn unnamed2named_2() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let model: EntityModel = entity.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(123, model.ghost_int);
+ assert_eq!(321, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn unnamed2named_reverse() {
+ let dto = TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(321, entity.another_int);
+}
+
+#[test]
+fn unnamed2named_reverse_2() {
+ let model = TupleEntityModel (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let named: Entity = model.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn unnamed2named_ref() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.some_int);
+ assert_eq!(entity.1 as i32, dto.another_int);
+ assert_eq!(entity.0, dto.ghost_int);
+ assert_eq!(entity.1 as i16, dto.ghost_int_2);
+ assert_eq!(456.0, dto.ghost_float);
+}
+
+#[test]
+fn unnamed2named_ref_2() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let model: EntityModel = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, model.some_int);
+ assert_eq!(entity.1 as i32, model.another_int);
+ assert_eq!(entity.0, model.ghost_int);
+ assert_eq!(entity.1 as i16, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn unnamed2named_reverse_ref() {
+ let dto = &TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let named: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, named.some_int);
+ assert_eq!(dto.1 as i32, named.another_int);
+}
+
+#[test]
+fn unnamed2named_reverse_ref_2() {
+ let model = &TupleEntityModel (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let named: Entity = model.try_into().unwrap();
+
+ assert_eq!(model.0, named.some_int);
+ assert_eq!(model.1 as i32, named.another_int);
+}
+
+#[test]
+fn existing_named2named() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: EntityModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(123, model.ghost_int);
+ assert_eq!(321, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn existing_named2named_reverse() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let mut named: Entity = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: EntityModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(named.some_int, model.some_int);
+ assert_eq!(named.another_int, model.another_int);
+ assert_eq!(named.some_int, model.ghost_int);
+ assert_eq!(named.another_int as i16, model.ghost_int_2);
+ assert_eq!(4567.0, model.ghost_float);
+}
+
+#[test]
+fn existing_named2named_reverse_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let mut named: Entity = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int as i32, named.another_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let mut model: TupleEntityModel = Default::default();
+ entity.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+ assert_eq!(123, model.2);
+ assert_eq!(321, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn existing_unnamed2unnamed_reverse() {
+ let dto = TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let mut model: TupleEntityModel = Default::default();
+ entity.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(entity.0, model.0);
+ assert_eq!(entity.1, model.1);
+ assert_eq!(entity.0, model.2);
+ assert_eq!(entity.1 as i16, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn existing_unnamed2unnamed_reverse_ref() {
+ let dto = &TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1);
+}
+
+#[test]
+fn existing_named2unnamed() {
+ let named = Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: TupleEntityModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+ assert_eq!(123, model.2);
+ assert_eq!(321, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn existing_named2unnamed_reverse() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1);
+}
+
+#[test]
+fn existing_named2unnamed_ref() {
+ let named = &Entity {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: TupleEntityModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(named.some_int, model.0);
+ assert_eq!(named.another_int as i16, model.1);
+ assert_eq!(named.some_int, model.2);
+ assert_eq!(named.another_int as i16, model.3);
+ assert_eq!(456.0, model.4);
+}
+
+#[test]
+fn existing_named2unnamed_reverse_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int: 321,
+ ghost_int: 456,
+ ghost_int_2: 654,
+ ghost_float: 789.0
+ };
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.some_int, entity.0);
+ assert_eq!(dto.another_int as i16, entity.1);
+}
+
+#[test]
+fn existing_unnamed2named_2() {
+ let entity = TupleEntity (
+ 123,
+ 321,
+ );
+
+ let mut model: EntityModel = Default::default();
+ entity.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(123, model.ghost_int);
+ assert_eq!(321, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn existing_unnamed2named_reverse() {
+ let dto = TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let mut named: Entity = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+}
+
+#[test]
+fn existing_unnamed2named_ref() {
+ let entity = &TupleEntity (
+ 123,
+ 321,
+ );
+
+ let mut model: EntityModel = Default::default();
+ entity.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(entity.0, model.some_int);
+ assert_eq!(entity.1 as i32, model.another_int);
+ assert_eq!(entity.0, model.ghost_int);
+ assert_eq!(entity.1 as i16, model.ghost_int_2);
+ assert_eq!(456.0, model.ghost_float);
+}
+
+#[test]
+fn existing_unnamed2named_reverse_ref() {
+ let dto = &TupleEntityDto (
+ 123,
+ 321,
+ 456,
+ 654,
+ 789.0
+ );
+
+ let mut named: Entity = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(dto.0, named.some_int);
+ assert_eq!(dto.1 as i32, named.another_int);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/14_parent_attr_tests_fallible.rs b/o2o-tests/tests/14_parent_attr_tests_fallible.rs
new file mode 100644
index 0000000..a7b1285
--- /dev/null
+++ b/o2o-tests/tests/14_parent_attr_tests_fallible.rs
@@ -0,0 +1,379 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(o2o)]
+#[try_map(EntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct Entity {
+ parent_int: i32,
+ #[parent]
+ base: BaseEntity,
+ #[parent]
+ child: Child,
+}
+
+#[derive(o2o)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct BaseEntity {
+ #[o2o(parent)]
+ base: Base,
+ base_entity_int: i32,
+}
+
+#[derive(o2o)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct Base {
+ #[map(base_int)]
+ base_int_2: i32,
+ another_base_int: i32,
+}
+
+#[derive(o2o)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(Default)]
+struct EntityDto {
+ pub parent_int: i32,
+ pub base_int: i32,
+ pub another_base_int: i32,
+ pub base_entity_int: i32,
+ pub child_int: i32,
+ pub another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct TupleEntity(
+ i32,
+ #[parent]
+ TupleBaseEntity,
+ #[o2o(parent)]
+ TupleChild
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct TupleBaseEntity(
+ #[parent]
+ TupleBase,
+ #[map(3)]
+ i32
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct TupleBase(
+ #[map(1)] i32,
+ #[map(2)] i16
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct TupleChild(
+ #[map(4)] i32,
+ #[map(5)] i16
+);
+
+#[derive(Default)]
+struct TupleEntityDto (i32, i32, i16, i32, i32, i16);
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.base.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.base.another_base_int);
+ assert_eq!(dto.base_entity_int, entity.base.base_entity_int);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.base.base_int_2, dto.base_int);
+ assert_eq!(entity.base.base.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.base_entity_int, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.0.0);
+ assert_eq!(dto.2, entity.1.0.1);
+ assert_eq!(dto.3, entity.1.1);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.0.0, dto.1);
+ assert_eq!(entity.1.0.1, dto.2);
+ assert_eq!(entity.1.1, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
+
+#[test]
+fn existing_named2named() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.base.base_int_2, dto.base_int);
+ assert_eq!(entity.base.base.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.base_entity_int, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.0.0, dto.1);
+ assert_eq!(entity.1.0.1, dto.2);
+ assert_eq!(entity.1.1, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/15_multiple_parent_attr_tests_fallible.rs b/o2o-tests/tests/15_multiple_parent_attr_tests_fallible.rs
new file mode 100644
index 0000000..b5bfb27
--- /dev/null
+++ b/o2o-tests/tests/15_multiple_parent_attr_tests_fallible.rs
@@ -0,0 +1,826 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(o2o)]
+#[o2o(
+ try_map(EntityDto, String),
+ try_map(TupleEntityDto, String),
+ try_map(EntityModel, String),
+ try_into_existing(EntityDto, String),
+ try_into_existing(TupleEntityDto, String),
+)]
+struct Entity {
+ #[map(TupleEntityDto| 0)]
+ parent_int: i32,
+ #[parent(EntityDto)]
+ #[parent(TupleEntityDto)]
+ #[map_ref(EntityModel| ~.clone())]
+ base: BaseEntity,
+ #[parent(EntityDto)]
+ #[parent(TupleEntityDto)]
+ #[map_ref(EntityModel| ~.clone())]
+ child: Child,
+}
+
+struct EntityModel {
+ parent_int: i32,
+ base: BaseEntity,
+ child: Child,
+}
+
+#[derive(Clone, o2o)]
+#[try_from(EntityDto, String)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct BaseEntity {
+ #[parent]
+ base: Base,
+ #[map(TupleEntityDto| 3)]
+ base_entity_int: i32,
+}
+
+#[derive(Clone, o2o)]
+#[try_from(EntityDto, String)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct Base {
+ #[map(EntityDto| base_int)]
+ #[map(TupleEntityDto| 1)]
+ base_int_2: i32,
+ #[from(TupleEntityDto| @.2 as i32)]
+ #[into(TupleEntityDto| 2, ~ as i16)]
+ another_base_int: i32,
+}
+
+#[derive(Clone, o2o)]
+#[try_from(EntityDto, String)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct Child {
+ #[map(TupleEntityDto| 4)]
+ child_int: i32,
+ #[from(TupleEntityDto| @.5 as i32)]
+ #[into(TupleEntityDto| 5, ~ as i16)]
+ another_child_int: i32,
+}
+
+#[derive(Default)]
+struct EntityDto {
+ pub parent_int: i32,
+ pub base_int: i32,
+ pub another_base_int: i32,
+ pub base_entity_int: i32,
+ pub child_int: i32,
+ pub another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(TupleEntityDto, String)]
+#[try_map(EntityDto as {}, String)]
+#[try_into_existing(TupleEntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct TupleEntity(
+ #[map(EntityDto| parent_int)]
+ i32,
+ #[parent]
+ TupleBaseEntity,
+ #[parent]
+ TupleChild
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct TupleBaseEntity(
+ #[parent]
+ TupleBase,
+ #[map(TupleEntityDto| 3)]
+ #[map(EntityDto| base_entity_int)]
+ i32
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct TupleBase(
+ #[map(TupleEntityDto| 1)]
+ #[map(EntityDto| base_int)]
+ i32,
+ #[map(TupleEntityDto| 2)]
+ #[from(EntityDto| @.another_base_int as i16)]
+ #[into(EntityDto| another_base_int, ~ as i32)]
+ i16
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct TupleChild(
+ #[map(TupleEntityDto| 4)]
+ #[map(EntityDto| child_int)]
+ i32,
+ #[map(TupleEntityDto| 5)]
+ #[from(EntityDto| @.another_child_int as i16)]
+ #[into(EntityDto| another_child_int, ~ as i32)]
+ i16
+);
+
+#[derive(Default)]
+struct TupleEntityDto (i32, i32, i16, i32, i32, i16);
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn named2unnamed() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn named2unnamed_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.base.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.base.another_base_int);
+ assert_eq!(dto.base_entity_int, entity.base.base_entity_int);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn named2unnamed_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.0);
+ assert_eq!(dto.base_int, entity.1.0.0);
+ assert_eq!(dto.another_base_int as i16, entity.1.0.1);
+ assert_eq!(dto.base_entity_int, entity.1.1);
+ assert_eq!(dto.child_int, entity.2.0);
+ assert_eq!(dto.another_child_int as i16, entity.2.1);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.base.base_int_2, dto.base_int);
+ assert_eq!(entity.base.base.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.base_entity_int, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn named2unnamed_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.0);
+ assert_eq!(entity.base.base.base_int_2, dto.1);
+ assert_eq!(entity.base.base.another_base_int as i16, dto.2);
+ assert_eq!(entity.base.base_entity_int, dto.3);
+ assert_eq!(entity.child.child_int, dto.4);
+ assert_eq!(entity.child.another_child_int as i16, dto.5);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn unnamed2named() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn unnamed2named_reverse() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.0.0);
+ assert_eq!(dto.2, entity.1.0.1);
+ assert_eq!(dto.3, entity.1.1);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
+
+#[test]
+fn unnamed2named_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.parent_int);
+ assert_eq!(dto.1, entity.base.base.base_int_2);
+ assert_eq!(dto.2 as i32, entity.base.base.another_base_int);
+ assert_eq!(dto.3, entity.base.base_entity_int);
+ assert_eq!(dto.4, entity.child.child_int);
+ assert_eq!(dto.5 as i32, entity.child.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.0.0, dto.1);
+ assert_eq!(entity.1.0.1, dto.2);
+ assert_eq!(entity.1.1, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
+
+#[test]
+fn unnamed2named_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.parent_int);
+ assert_eq!(entity.1.0.0, dto.base_int);
+ assert_eq!(entity.1.0.1 as i32, dto.another_base_int);
+ assert_eq!(entity.1.1, dto.base_entity_int);
+ assert_eq!(entity.2.0, dto.child_int);
+ assert_eq!(entity.2.1 as i32, dto.another_child_int);
+}
+
+#[test]
+fn named2named_2() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let model: EntityModel = entity.try_into().unwrap();
+
+ assert_eq!(123, model.parent_int);
+ assert_eq!(321, model.base.base.base_int_2);
+ assert_eq!(456, model.base.base.another_base_int);
+ assert_eq!(654, model.base.base_entity_int);
+ assert_eq!(789, model.child.child_int);
+ assert_eq!(987, model.child.another_child_int);
+}
+
+#[test]
+fn named2named_2_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let model: EntityModel = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, model.parent_int);
+ assert_eq!(entity.base.base.base_int_2, model.base.base.base_int_2);
+ assert_eq!(entity.base.base.another_base_int, model.base.base.another_base_int);
+ assert_eq!(entity.base.base_entity_int, model.base.base_entity_int);
+ assert_eq!(entity.child.child_int, model.child.child_int);
+ assert_eq!(entity.child.another_child_int, model.child.another_child_int);
+}
+
+#[test]
+fn named2named_2_reverse() {
+ let model = EntityModel {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let entity: Entity = model.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_2_ref_reverse() {
+ let model = &EntityModel {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let entity: Entity = model.try_into().unwrap();
+
+ assert_eq!(model.parent_int, entity.parent_int);
+ assert_eq!(model.base.base.base_int_2, entity.base.base.base_int_2);
+ assert_eq!(model.base.base.another_base_int, entity.base.base.another_base_int);
+ assert_eq!(model.base.base_entity_int, entity.base.base_entity_int);
+ assert_eq!(model.child.child_int, entity.child.child_int);
+ assert_eq!(model.child.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_named2named() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn existing_named2unnamed() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.base.base_int_2, dto.base_int);
+ assert_eq!(entity.base.base.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.base_entity_int, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn existing_named2unnamed_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.parent_int, dto.0);
+ assert_eq!(entity.base.base.base_int_2, dto.1);
+ assert_eq!(entity.base.base.another_base_int as i16, dto.2);
+ assert_eq!(entity.base.base_entity_int, dto.3);
+ assert_eq!(entity.child.child_int, dto.4);
+ assert_eq!(entity.child.another_child_int as i16, dto.5);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn existing_unnamed2named() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.0.0, dto.1);
+ assert_eq!(entity.1.0.1, dto.2);
+ assert_eq!(entity.1.1, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
+
+#[test]
+fn existing_unnamed2named_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.0, dto.parent_int);
+ assert_eq!(entity.1.0.0, dto.base_int);
+ assert_eq!(entity.1.0.1 as i32, dto.another_base_int);
+ assert_eq!(entity.1.1, dto.base_entity_int);
+ assert_eq!(entity.2.0, dto.child_int);
+ assert_eq!(entity.2.1 as i32, dto.another_child_int);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/16_parent_attr_mixed_struct_kinds_tests_fallible.rs b/o2o-tests/tests/16_parent_attr_mixed_struct_kinds_tests_fallible.rs
new file mode 100644
index 0000000..5dc0143
--- /dev/null
+++ b/o2o-tests/tests/16_parent_attr_mixed_struct_kinds_tests_fallible.rs
@@ -0,0 +1,380 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(o2o)]
+#[o2o(try_map(EntityDto, String))]
+#[o2o(try_into_existing(EntityDto, String))]
+struct Entity {
+ parent_int: i32,
+ #[o2o(parent)]
+ base: TupleBaseEntity,
+ #[parent]
+ child: Child,
+}
+
+#[derive(o2o)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct TupleBaseEntity(
+ #[parent]
+ Base,
+ #[map(base_entity_int)]
+ i32
+);
+
+#[derive(o2o)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct Base {
+ #[map(base_int)]
+ base_int_2: i32,
+ another_base_int: i32,
+}
+
+#[derive(o2o)]
+#[try_from(EntityDto, String)]
+#[try_into_existing(EntityDto, String)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct TupleEntity(
+ i32,
+ #[parent]
+ BaseEntity,
+ #[parent]
+ TupleChild
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct BaseEntity {
+ #[parent]
+ base: TupleBase,
+ #[map(3)]
+ base_entity_int: i32,
+}
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct TupleBase(
+ #[map(1)] i32,
+ #[map(2)] i16
+);
+
+#[derive(o2o)]
+#[try_from(TupleEntityDto, String)]
+#[try_into_existing(TupleEntityDto, String)]
+struct TupleChild(
+ #[map(4)] i32,
+ #[map(5)] i16
+);
+
+#[derive(Default)]
+struct EntityDto {
+ parent_int: i32,
+ base_int: i32,
+ another_base_int: i32,
+ base_entity_int: i32,
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(Default)]
+struct TupleEntityDto (i32, i32, i16, i32, i32, i16);
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.0.base_int_2);
+ assert_eq!(456, entity.base.0.another_base_int);
+ assert_eq!(654, entity.base.1);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: TupleBaseEntity (
+ Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ 654
+ ),
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.0.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.0.another_base_int);
+ assert_eq!(dto.base_entity_int, entity.base.1);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: TupleBaseEntity (
+ Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ 654
+ ),
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.0.base_int_2, dto.base_int);
+ assert_eq!(entity.base.0.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.1, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.base.0);
+ assert_eq!(456, entity.1.base.1);
+ assert_eq!(654, entity.1.base_entity_int);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let entity = TupleEntity(
+ 123,
+ BaseEntity {
+ base: TupleBase (
+ 321,
+ 456
+ ),
+ base_entity_int: 654
+ },
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.base.0);
+ assert_eq!(dto.2, entity.1.base.1);
+ assert_eq!(dto.3, entity.1.base_entity_int);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ BaseEntity {
+ base: TupleBase (
+ 321,
+ 456
+ ),
+ base_entity_int: 654
+ },
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.base.0, dto.1);
+ assert_eq!(entity.1.base.1, dto.2);
+ assert_eq!(entity.1.base_entity_int, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
+
+#[test]
+fn existing_named2named() {
+ let entity = Entity {
+ parent_int: 123,
+ base: TupleBaseEntity (
+ Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ 654
+ ),
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: TupleBaseEntity (
+ Base {
+ base_int_2: 321,
+ another_base_int: 456
+ },
+ 654
+ ),
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let mut dto: EntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.0.base_int_2, dto.base_int);
+ assert_eq!(entity.base.0.another_base_int, dto.another_base_int);
+ assert_eq!(entity.base.1, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let entity = TupleEntity(
+ 123,
+ BaseEntity {
+ base: TupleBase (
+ 321,
+ 456
+ ),
+ base_entity_int: 654
+ },
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let entity = &TupleEntity(
+ 123,
+ BaseEntity {
+ base: TupleBase (
+ 321,
+ 456
+ ),
+ base_entity_int: 654
+ },
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let mut dto: TupleEntityDto = Default::default();
+ entity.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.base.0, dto.1);
+ assert_eq!(entity.1.base.1, dto.2);
+ assert_eq!(entity.1.base_entity_int, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/17_generic_path_tests_fallible.rs b/o2o-tests/tests/17_generic_path_tests_fallible.rs
new file mode 100644
index 0000000..dbf0ce1
--- /dev/null
+++ b/o2o-tests/tests/17_generic_path_tests_fallible.rs
@@ -0,0 +1,227 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+
+#[derive(Default)]
+struct Parent {
+ child: Child,
+ parent_int: T,
+}
+
+#[derive(Default)]
+struct ParentModel {
+ child_diff: Child,
+ parent_int: T,
+}
+
+#[derive(Default)]
+struct Child {
+ child_int: T,
+ another_child_int: T,
+}
+
+#[derive(o2o)]
+#[o2o(
+ try_map(Parent::, String),
+ try_map(ParentModel::, String),
+ try_into_existing(Parent::, String),
+ try_into_existing(ParentModel::, String),
+)]
+struct ParentDto {
+ parent_int: i32,
+ #[o2o(
+ from(Parent::| (&@.child).try_into().unwrap()),
+ into(Parent::| child, (&@.diff_child).try_into().unwrap()),
+ from(ParentModel::| (&@.child_diff).try_into().unwrap()),
+ into(ParentModel::| child_diff, (&@.diff_child).try_into().unwrap()),
+ )]
+ diff_child: ChildDto,
+}
+
+#[derive(o2o)]
+#[try_map(Child::, String)]
+#[try_map(Child::, String)]
+struct ChildDto {
+ #[o2o(from(Child::| @.child_int as i16))]
+ #[o2o(into(Child::| @.child_int as i32))]
+ child_int: i16,
+ #[from(Child::| @.another_child_int as i8)]
+ #[into(Child::| another_child_int, @.diff_another_child_int as i32)]
+ #[from(Child::| @.another_child_int as i8)]
+ #[into(Child::| another_child_int, @.diff_another_child_int as i16)]
+ diff_another_child_int: i8,
+}
+
+#[test]
+fn named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let p: Parent:: = dto.try_into().unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let model: ParentModel:: = dto.try_into().unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
+
+#[test]
+fn named2named_different_name_and_type_reverse() {
+ let p = Parent{
+ parent_int: 987,
+ child: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+
+ let model = ParentModel{
+ parent_int: 987,
+ child_diff: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = model.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+}
+
+#[test]
+fn named2named_different_name_and_type_ref() {
+ let dto = &ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let p: Parent:: = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, p.parent_int);
+ assert_eq!(dto.diff_child.child_int, p.child.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, p.child.another_child_int as i8);
+
+ let model: ParentModel:: = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, model.parent_int);
+ assert_eq!(dto.diff_child.child_int, model.child_diff.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, model.child_diff.another_child_int as i8);
+}
+
+#[test]
+fn named2named_different_name_and_type_reverse_ref() {
+ let p = &Parent{
+ parent_int: 987,
+ child: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(p.parent_int, dto.parent_int);
+ assert_eq!(p.child.child_int, dto.diff_child.child_int);
+ assert_eq!(p.child.another_child_int, dto.diff_child.diff_another_child_int as i16);
+
+ let model = &ParentModel{
+ parent_int: 987,
+ child_diff: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = model.try_into().unwrap();
+
+ assert_eq!(model.parent_int, dto.parent_int);
+ assert_eq!(model.child_diff.child_int, dto.diff_child.child_int as i32);
+ assert_eq!(model.child_diff.another_child_int, dto.diff_child.diff_another_child_int as i32);
+}
+
+#[test]
+fn existing_named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut p: Parent:: = Default::default();
+ dto.try_into_existing(&mut p).unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut model: ParentModel:: = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
+
+#[test]
+fn existing_named2named_different_name_and_type_ref() {
+ let dto = &ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut p: Parent:: = Default::default();
+ dto.try_into_existing(&mut p).unwrap();
+
+ assert_eq!(dto.parent_int, p.parent_int);
+ assert_eq!(dto.diff_child.child_int, p.child.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, p.child.another_child_int as i8);
+
+ let mut model: ParentModel:: = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.parent_int, model.parent_int);
+ assert_eq!(dto.diff_child.child_int, model.child_diff.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, model.child_diff.another_child_int as i8);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/18_where_attr_tests_fallible.rs b/o2o-tests/tests/18_where_attr_tests_fallible.rs
new file mode 100644
index 0000000..328172b
--- /dev/null
+++ b/o2o-tests/tests/18_where_attr_tests_fallible.rs
@@ -0,0 +1,232 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Parent {
+ child: TChild,
+ parent_int: T,
+}
+
+#[derive(Default)]
+struct ParentModel {
+ child_diff: Child,
+ parent_int: i32,
+}
+
+#[derive(Default)]
+struct Child {
+ child_int: T,
+ another_child_int: T,
+}
+
+#[derive(o2o)]
+#[o2o(
+ try_map(Parent::, i32>, String),
+ try_map(ParentModel::, String),
+ try_into_existing(Parent::, i32>, String),
+ try_into_existing(ParentModel::, String),
+ where_clause(T: Copy),
+)]
+struct ParentDto where T: Copy {
+ parent_int: i32,
+ #[o2o(
+ from(Parent::, i32>| (&@.child).try_into().unwrap()),
+ owned_into(Parent::, i32>| child, ~.try_into().unwrap()),
+ ref_into(Parent::, i32>| child, (&@.diff_child).try_into().unwrap()),
+ from_owned(ParentModel::| @.child_diff.try_into().unwrap()),
+ )]
+ #[o2o(from_ref(ParentModel::| (&@.child_diff).try_into().unwrap()))]
+ #[into(ParentModel::| child_diff, (&@.diff_child).try_into().unwrap())]
+ diff_child: ChildDto,
+}
+
+#[derive(o2o)]
+#[try_map(Child::, String)]
+#[where_clause(T: Copy)]
+struct ChildDto where T: Copy {
+ child_int: T,
+ #[map(another_child_int)]
+ diff_another_child_int: T,
+}
+
+#[test]
+fn named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let p: Parent::, i32> = dto.try_into().unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let model: ParentModel:: = dto.try_into().unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
+
+#[test]
+fn named2named_different_name_and_type_reverse() {
+ let p = Parent{
+ parent_int: 987,
+ child: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+
+ let model = ParentModel{
+ parent_int: 987,
+ child_diff: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = model.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+}
+
+#[test]
+fn named2named_different_name_and_type_ref() {
+ let dto = &ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let p: Parent::, i32> = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, p.parent_int);
+ assert_eq!(dto.diff_child.child_int, p.child.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, p.child.another_child_int);
+
+ let model: ParentModel:: = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, model.parent_int);
+ assert_eq!(dto.diff_child.child_int, model.child_diff.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, model.child_diff.another_child_int as i16);
+}
+
+#[test]
+fn named2named_different_name_and_type_reverse_ref() {
+ let p = &Parent{
+ parent_int: 987,
+ child: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(p.parent_int, dto.parent_int);
+ assert_eq!(p.child.child_int, dto.diff_child.child_int);
+ assert_eq!(p.child.another_child_int, dto.diff_child.diff_another_child_int);
+
+ let model = &ParentModel{
+ parent_int: 987,
+ child_diff: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = model.try_into().unwrap();
+
+ assert_eq!(model.parent_int, dto.parent_int);
+ assert_eq!(model.child_diff.child_int, dto.diff_child.child_int);
+ assert_eq!(model.child_diff.another_child_int, dto.diff_child.diff_another_child_int);
+}
+
+#[test]
+fn existing_named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut p: Parent::, i32> = Default::default();
+ dto.try_into_existing(&mut p).unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut model: ParentModel:: = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
+
+#[test]
+fn existing_named2named_different_name_and_type_ref() {
+ let dto = &ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut p: Parent::, i32> = Default::default();
+ dto.try_into_existing(&mut p).unwrap();
+
+ assert_eq!(dto.parent_int, p.parent_int);
+ assert_eq!(dto.diff_child.child_int, p.child.child_int);
+ assert_eq!(dto.diff_child.diff_another_child_int, p.child.another_child_int);
+
+ let dto = &ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut model: ParentModel:: = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.parent_int, model.parent_int);
+ assert_eq!(dto.diff_child.child_int, model.child_diff.child_int);
+ assert_eq!(dto.diff_child.diff_another_child_int, model.child_diff.another_child_int);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/19_deep_ghost_attr_tests_fallible.rs b/o2o-tests/tests/19_deep_ghost_attr_tests_fallible.rs
new file mode 100644
index 0000000..016faf4
--- /dev/null
+++ b/o2o-tests/tests/19_deep_ghost_attr_tests_fallible.rs
@@ -0,0 +1,412 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Car {
+ number_of_doors: i8,
+ vehicle: Vehicle
+}
+#[derive(Default)]
+struct Vehicle {
+ number_of_seats: i16,
+ machine: Machine,
+}
+#[derive(Default)]
+struct Machine {
+ id: i32,
+ brand: String,
+ year: i16,
+}
+
+#[derive(o2o)]
+#[try_map(Car, String)]
+#[try_into_existing(Car, String)]
+#[children(vehicle: Vehicle, vehicle.machine: Machine)]
+#[ghosts(vehicle.machine@id: { 321 })]
+struct CarDto {
+ number_of_doors: i8,
+
+ #[child(vehicle)]
+ number_of_seats: i16,
+
+ #[child(vehicle.machine)]
+ #[map(~.clone())]
+ brand: String,
+
+ #[child(vehicle.machine)]
+ year: i16
+}
+
+#[derive(Default)]
+pub struct Base {
+ pub id: u32,
+ pub name: String,
+}
+
+#[derive(Default)]
+pub struct League {
+ pub base: Base,
+}
+
+#[derive(Default)]
+pub struct Division {
+ pub base: Base,
+ pub league_id: u32,
+ pub league: League,
+}
+
+#[derive(Default)]
+pub struct Team {
+ pub base: Base,
+ pub division_id: u32,
+ pub division: Division,
+}
+
+#[derive(o2o)]
+#[o2o(
+ try_map(Team, String),
+ try_into_existing(Team, String),
+ children(base: Base, division: Division, division.base: Base, division.league: League, division.league.base: Base),
+ ghosts_owned(
+ division_id: { @.division.id },
+ division.base@id: { @.division.id },
+ division.base@name: { @.division.name },
+ division@league_id: { @.league.id },
+ division.league.base@id: { @.league.id },
+ division.league.base@name: { @.league.name }
+ ),
+ ghosts_ref(
+ division_id: { @.division.id },
+ division.base@id: { @.division.id },
+ division.base@name: { @.division.name.clone() },
+ division@league_id: { @.league.id },
+ division.league.base@id: { @.league.id },
+ division.league.base@name: { @.league.name.clone() }
+ )
+)]
+pub struct TeamDto {
+ #[child(base)]
+ id: u32,
+
+ #[child(base)]
+ #[map(~.clone())]
+ name: String,
+
+ #[ghost((&@.division.base).try_into().unwrap())]
+ division: DivisionDto,
+
+ #[ghost((&@.division.league.base).try_into().unwrap())]
+ league: LeagueDto,
+}
+
+#[derive(o2o)]
+#[from(Base)]
+pub struct LeagueDto {
+ id: u32,
+ #[from(~.clone())]
+ name: String,
+}
+
+#[derive(o2o)]
+#[from(Base)]
+pub struct DivisionDto {
+ id: u32,
+ #[from(~.clone())]
+ name: String,
+}
+
+#[test]
+fn named2named() {
+ let car = Car {
+ number_of_doors: 2,
+ vehicle: Vehicle {
+ number_of_seats: 4,
+ machine: Machine {
+ id: 123,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960
+ }
+ }
+ };
+
+ let car_dto: CarDto = car.try_into().unwrap();
+
+ assert_eq!(2, car_dto.number_of_doors);
+ assert_eq!(4, car_dto.number_of_seats);
+ assert_eq!("Trabant", car_dto.brand);
+ assert_eq!(1960, car_dto.year);
+}
+
+#[test]
+fn named2named_reverse() {
+ let car_dto = CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960
+ };
+
+ let car: Car = car_dto.try_into().unwrap();
+
+ assert_eq!(2, car.number_of_doors);
+ assert_eq!(4, car.vehicle.number_of_seats);
+ assert_eq!("Trabant", car.vehicle.machine.brand);
+ assert_eq!(1960, car.vehicle.machine.year);
+ assert_eq!(321, car.vehicle.machine.id);
+}
+
+#[test]
+fn named2named_ref() {
+ let car = &Car {
+ number_of_doors: 2,
+ vehicle: Vehicle {
+ number_of_seats: 4,
+ machine: Machine {
+ id: 123,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960
+ }
+ }
+ };
+
+ let car_dto: CarDto = car.try_into().unwrap();
+
+ assert_eq!(car.number_of_doors, car_dto.number_of_doors);
+ assert_eq!(car.vehicle.number_of_seats, car_dto.number_of_seats);
+ assert_eq!(car.vehicle.machine.brand, car_dto.brand);
+ assert_eq!(car.vehicle.machine.year, car_dto.year);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let car_dto = &CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960
+ };
+
+ let car: Car = car_dto.try_into().unwrap();
+
+ assert_eq!(car_dto.number_of_doors, car.number_of_doors);
+ assert_eq!(car_dto.number_of_seats, car.vehicle.number_of_seats);
+ assert_eq!(car_dto.brand, car.vehicle.machine.brand);
+ assert_eq!(car_dto.year, car.vehicle.machine.year);
+ assert_eq!(321, car.vehicle.machine.id);
+}
+
+#[test]
+fn existing_named2named() {
+ let car_dto = CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960
+ };
+
+ let mut car: Car = Default::default();
+ car_dto.try_into_existing(&mut car).unwrap();
+
+ assert_eq!(2, car.number_of_doors);
+ assert_eq!(4, car.vehicle.number_of_seats);
+ assert_eq!("Trabant", car.vehicle.machine.brand);
+ assert_eq!(1960, car.vehicle.machine.year);
+ assert_eq!(321, car.vehicle.machine.id);
+}
+
+#[test]
+fn existing_named2named_reverse() {
+ let car_dto = &CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960
+ };
+
+ let mut car: Car = Default::default();
+ car_dto.try_into_existing(&mut car).unwrap();
+
+ assert_eq!(car_dto.number_of_doors, car.number_of_doors);
+ assert_eq!(car_dto.number_of_seats, car.vehicle.number_of_seats);
+ assert_eq!(car_dto.brand, car.vehicle.machine.brand);
+ assert_eq!(car_dto.year, car.vehicle.machine.year);
+ assert_eq!(321, car.vehicle.machine.id);
+}
+
+#[test]
+fn named2named_2() {
+ let team = Team {
+ base: Base {
+ id: 123,
+ name: "Test".try_into().unwrap()
+ },
+ division_id: 456,
+ division: Division {
+ base: Base {
+ id: 456,
+ name: "TestDivision".try_into().unwrap()
+ },
+ league_id: 789,
+ league: League {
+ base: Base {
+ id: 789,
+ name: "TestLeague".try_into().unwrap()
+ }
+ }
+ }
+ };
+
+ let team_dto: TeamDto = team.try_into().unwrap();
+
+ assert_eq!(123, team_dto.id);
+ assert_eq!("Test", team_dto.name);
+ assert_eq!(456, team_dto.division.id);
+ assert_eq!("TestDivision", team_dto.division.name);
+ assert_eq!(789, team_dto.league.id);
+ assert_eq!("TestLeague", team_dto.league.name);
+}
+
+#[test]
+fn named2named_reverse_2() {
+ let team_dto = TeamDto {
+ id: 123,
+ name: "Test".try_into().unwrap(),
+ division: DivisionDto {
+ id: 456,
+ name: "TestDivision".try_into().unwrap(),
+ },
+ league: LeagueDto {
+ id: 789,
+ name: "TestLeague".try_into().unwrap()
+ }
+ };
+
+ let team: Team = team_dto.try_into().unwrap();
+
+ assert_eq!(123, team.base.id);
+ assert_eq!("Test", team.base.name);
+ assert_eq!(456, team.division_id);
+ assert_eq!(456, team.division.base.id);
+ assert_eq!("TestDivision", team.division.base.name);
+ assert_eq!(789, team.division.league_id);
+ assert_eq!(789, team.division.league.base.id);
+ assert_eq!("TestLeague", team.division.league.base.name);
+}
+
+#[test]
+fn named2named_ref_2() {
+ let team = &Team {
+ base: Base {
+ id: 123,
+ name: "Test".try_into().unwrap()
+ },
+ division_id: 456,
+ division: Division {
+ base: Base {
+ id: 456,
+ name: "TestDivision".try_into().unwrap()
+ },
+ league_id: 789,
+ league: League {
+ base: Base {
+ id: 789,
+ name: "TestLeague".try_into().unwrap()
+ }
+ }
+ }
+ };
+
+ let team_dto: TeamDto = team.try_into().unwrap();
+
+ assert_eq!(team.base.id, team_dto.id);
+ assert_eq!(team.base.name, team_dto.name);
+ assert_eq!(team.division_id, team_dto.division.id);
+ assert_eq!(team.division.base.id, team_dto.division.id);
+ assert_eq!(team.division.base.name, team_dto.division.name);
+ assert_eq!(team.division.league_id, team_dto.league.id);
+ assert_eq!(team.division.league.base.id, team_dto.league.id);
+ assert_eq!(team.division.league.base.name, team_dto.league.name);
+}
+
+#[test]
+fn named2named_ref_reverse_2() {
+ let team_dto = &TeamDto {
+ id: 123,
+ name: "Test".try_into().unwrap(),
+ division: DivisionDto {
+ id: 456,
+ name: "TestDivision".try_into().unwrap(),
+ },
+ league: LeagueDto {
+ id: 789,
+ name: "TestLeague".try_into().unwrap()
+ }
+ };
+
+ let team: Team = team_dto.try_into().unwrap();
+
+ assert_eq!(team_dto.id, team.base.id);
+ assert_eq!(team_dto.name, team.base.name);
+ assert_eq!(team_dto.division.id, team.division_id);
+ assert_eq!(team_dto.division.id, team.division.base.id);
+ assert_eq!(team_dto.division.name, team.division.base.name);
+ assert_eq!(team_dto.league.id, team.division.league_id);
+ assert_eq!(team_dto.league.id, team.division.league.base.id);
+ assert_eq!(team_dto.league.name, team.division.league.base.name);
+}
+
+#[test]
+fn existing_named2named_2() {
+ let team_dto = TeamDto {
+ id: 123,
+ name: "Test".try_into().unwrap(),
+ division: DivisionDto {
+ id: 456,
+ name: "TestDivision".try_into().unwrap(),
+ },
+ league: LeagueDto {
+ id: 789,
+ name: "TestLeague".try_into().unwrap()
+ }
+ };
+
+ let mut team: Team = Default::default();
+ team_dto.try_into_existing(&mut team).unwrap();
+
+ assert_eq!(123, team.base.id);
+ assert_eq!("Test", team.base.name);
+ assert_eq!(456, team.division_id);
+ assert_eq!(456, team.division.base.id);
+ assert_eq!("TestDivision", team.division.base.name);
+ assert_eq!(789, team.division.league_id);
+ assert_eq!(789, team.division.league.base.id);
+ assert_eq!("TestLeague", team.division.league.base.name);
+}
+
+#[test]
+fn existing_named2named_ref_2() {
+ let team_dto = &TeamDto {
+ id: 123,
+ name: "Test".try_into().unwrap(),
+ division: DivisionDto {
+ id: 456,
+ name: "TestDivision".try_into().unwrap(),
+ },
+ league: LeagueDto {
+ id: 789,
+ name: "TestLeague".try_into().unwrap()
+ }
+ };
+
+ let mut team: Team = Default::default();
+ team_dto.try_into_existing(&mut team).unwrap();
+
+ assert_eq!(team_dto.id, team.base.id);
+ assert_eq!(team_dto.name, team.base.name);
+ assert_eq!(team_dto.division.id, team.division_id);
+ assert_eq!(team_dto.division.id, team.division.base.id);
+ assert_eq!(team_dto.division.name, team.division.base.name);
+ assert_eq!(team_dto.league.id, team.division.league_id);
+ assert_eq!(team_dto.league.id, team.division.league.base.id);
+ assert_eq!(team_dto.league.name, team.division.league.base.name);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/1_bare_top_level_attr_tests_fallible.rs b/o2o-tests/tests/1_bare_top_level_attr_tests_fallible.rs
new file mode 100644
index 0000000..7bf2d9c
--- /dev/null
+++ b/o2o-tests/tests/1_bare_top_level_attr_tests_fallible.rs
@@ -0,0 +1,282 @@
+use std::num::TryFromIntError;
+
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct NamedStruct {
+ some_int: i32,
+ another_int: i32,
+}
+
+#[derive(Default)]
+struct UnnamedStruct(i32, i32);
+
+#[derive(Default)]
+struct NamedStructModel {
+ some_int: i32,
+ another_int: i32,
+}
+
+struct UnnamedStructModel(i32, i32);
+
+/// Test proper #[doc = ...] handling
+#[derive(o2o)]
+#[try_map(NamedStruct, anyhow::Error)]
+#[try_map(NamedStructModel, anyhow::Error)]
+#[try_into_existing(NamedStruct, anyhow::Error)]
+#[try_into_existing(NamedStructModel, anyhow::Error)]
+struct NamedStructDto {
+ /// Test proper #[doc = ...] handling
+ some_int: i32,
+ another_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(UnnamedStruct, TryFromIntError)]
+#[try_map(UnnamedStructModel, TryFromIntError)]
+#[try_into_existing(UnnamedStruct, TryFromIntError)]
+#[try_into_existing(UnnamedStructModel, TryFromIntError)]
+struct UnnamedStructDto(i32, i32);
+
+
+#[test]
+fn named2named() {
+ let dto = NamedStructDto {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+
+ let dto = NamedStructDto {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+}
+
+#[test]
+fn named2named_reverse() {
+ let named = NamedStruct {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.another_int);
+
+ let model = NamedStructModel {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.another_int);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &NamedStructDto {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.another_int);
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(named.some_int, model.some_int);
+ assert_eq!(named.another_int, model.another_int);
+}
+
+#[test]
+fn named2named_ref_reversed() {
+ let named = &NamedStruct {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.another_int);
+
+ let model = &NamedStructModel {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, dto.some_int);
+ assert_eq!(model.another_int, dto.another_int);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let dto = UnnamedStructDto(123, 321);
+
+ let unnamed: UnnamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, unnamed.0);
+ assert_eq!(321, unnamed.1);
+
+ let dto = UnnamedStructDto(123, 321);
+
+ let model: UnnamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+}
+
+#[test]
+fn unnamed2unnamed_reversed() {
+ let unnamed = UnnamedStruct(123, 321);
+
+ let dto: UnnamedStructDto = unnamed.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+
+ let model = UnnamedStructModel(123, 321);
+
+ let dto: UnnamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let dto = &UnnamedStructDto(123, 321);
+
+ let unnamed: UnnamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, unnamed.0);
+ assert_eq!(dto.1, unnamed.1);
+
+ let model: UnnamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, model.0);
+ assert_eq!(dto.1, model.1);
+}
+
+#[test]
+fn unnamed2unnamed_ref_reversed() {
+ let unnamed = &UnnamedStruct(123, 321);
+
+ let dto: UnnamedStructDto = unnamed.try_into().unwrap();
+
+ assert_eq!(unnamed.0, dto.0);
+ assert_eq!(unnamed.1, dto.1);
+
+ let model = &UnnamedStructModel(123, 321);
+
+ let dto: UnnamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(model.0, dto.0);
+ assert_eq!(model.1, dto.1);
+}
+
+#[test]
+fn existing_named2named() {
+ let dto = NamedStructDto {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+
+ let dto = NamedStructDto {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let dto = &NamedStructDto {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int, named.another_int);
+
+ let dto = &NamedStructDto {
+ some_int: 123,
+ another_int: 321,
+ };
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.some_int, model.some_int);
+ assert_eq!(dto.another_int, model.another_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let dto = UnnamedStructDto (123, 321);
+
+ let mut unnamed: UnnamedStruct = Default::default();
+
+ dto.try_into_existing(&mut unnamed).unwrap();
+
+ assert_eq!(123, unnamed.0);
+ assert_eq!(321, unnamed.1);
+
+ let dto = UnnamedStructDto (123, 321);
+
+ let model: UnnamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(321, model.1);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let dto = &UnnamedStructDto (123, 321);
+
+ let mut unnamed: UnnamedStruct = Default::default();
+
+ dto.try_into_existing(&mut unnamed).unwrap();
+
+ assert_eq!(dto.0, unnamed.0);
+ assert_eq!(dto.1, unnamed.1);
+
+ let dto = &UnnamedStructDto (123, 321);
+
+ let model: UnnamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, model.0);
+ assert_eq!(dto.1, model.1);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/20_as_type_attr_tests_fallible.rs b/o2o-tests/tests/20_as_type_attr_tests_fallible.rs
new file mode 100644
index 0000000..018a73a
--- /dev/null
+++ b/o2o-tests/tests/20_as_type_attr_tests_fallible.rs
@@ -0,0 +1,211 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct NamedStruct {
+ some_int: i32,
+ another_int: i32,
+ some_float: f32,
+ another_float: f64
+}
+
+#[derive(Default)]
+struct NamedStructModel {
+ some_int: i32,
+ different_int: i8,
+ some_float: f32,
+ another_float: f64
+}
+
+#[derive(o2o)]
+#[try_map(NamedStruct, String)]
+#[try_map(NamedStructModel, String)]
+#[try_into_existing(NamedStruct, String)]
+#[try_into_existing(NamedStructModel, String)]
+struct NamedStructDto {
+ some_int: i32,
+ #[o2o(
+ as_type(NamedStruct| another_int, i32),
+ as_type(NamedStructModel| i8),
+ )]
+ different_int: i16,
+ #[o2o(as_type(f32))]
+ some_float: f64,
+ #[o2o(as_type(another_float, f64))]
+ different_float: f32,
+}
+
+#[test]
+fn named2named_different_types(){
+ let dto = NamedStructDto{
+ some_int: 123,
+ different_int: 321,
+ some_float: 456.0,
+ different_float: 654.0
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+ assert_eq!(654.0, named.another_float);
+
+ let dto = NamedStructDto{
+ some_int: 123,
+ different_int: 127,
+ some_float: 456.0,
+ different_float: 654.0
+ };
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.different_int);
+ assert_eq!(456.0, model.some_float);
+ assert_eq!(654.0, model.another_float);
+}
+
+#[test]
+fn named2named_different_types_reverse(){
+ let named = NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ another_float: 654.0
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.different_int);
+ assert_eq!(456.0, dto.some_float);
+
+ let model = NamedStructModel{
+ some_int: 123,
+ different_int: 127,
+ some_float: 456.0,
+ another_float: 654.0
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(127, dto.different_int);
+ assert_eq!(456.0, dto.some_float);
+ assert_eq!(654.0, dto.different_float);
+}
+
+#[test]
+fn named2named_different_types_ref(){
+ let dto = &NamedStructDto{
+ some_int: 123,
+ different_int: 127,
+ some_float: 456.0,
+ different_float: 654.0
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.different_int, named.another_int as i16);
+ assert_eq!(dto.some_float, named.some_float as f64);
+ assert_eq!(dto.different_float, named.another_float as f32);
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, model.some_int);
+ assert_eq!(dto.different_int, model.different_int as i16);
+ assert_eq!(dto.some_float, model.some_float as f64);
+ assert_eq!(dto.different_float, model.another_float as f32);
+}
+
+#[test]
+fn named2named_different_types_reverse_ref(){
+ let named = &NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ another_float: 654.0
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.different_int as i32);
+ assert_eq!(named.some_float, dto.some_float as f32);
+ assert_eq!(named.another_float, dto.different_float as f64);
+
+ let model = &NamedStructModel{
+ some_int: 123,
+ different_int: 127,
+ some_float: 456.0,
+ another_float: 654.0
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, dto.some_int);
+ assert_eq!(model.different_int, dto.different_int as i8);
+ assert_eq!(model.some_float, dto.some_float as f32);
+ assert_eq!(model.another_float, dto.different_float as f64);
+}
+
+#[test]
+fn existing_named2named_different_types(){
+ let dto = NamedStructDto{
+ some_int: 123,
+ different_int: 321,
+ some_float: 456.0,
+ different_float: 654.0
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+ assert_eq!(654.0, named.another_float);
+
+ let dto = NamedStructDto{
+ some_int: 123,
+ different_int: 127,
+ some_float: 456.0,
+ different_float: 654.0
+ };
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.different_int);
+ assert_eq!(456.0, model.some_float);
+ assert_eq!(654.0, model.another_float);
+}
+
+#[test]
+fn existing_named2named_different_types_ref(){
+ let dto = &NamedStructDto{
+ some_int: 123,
+ different_int: 127,
+ some_float: 456.0,
+ different_float: 654.0
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.different_int, named.another_int as i16);
+ assert_eq!(dto.some_float, named.some_float as f64);
+ assert_eq!(dto.different_float, named.another_float as f32);
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.some_int, model.some_int);
+ assert_eq!(dto.different_int, model.different_int as i16);
+ assert_eq!(dto.some_float, model.some_float as f64);
+ assert_eq!(dto.different_float, model.another_float as f32);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/21_repeat_attr_tests_fallible.rs b/o2o-tests/tests/21_repeat_attr_tests_fallible.rs
new file mode 100644
index 0000000..7321dff
--- /dev/null
+++ b/o2o-tests/tests/21_repeat_attr_tests_fallible.rs
@@ -0,0 +1,287 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Car {
+ number_of_doors: i8,
+ vehicle: Vehicle
+}
+#[derive(Default)]
+struct Vehicle {
+ number_of_seats: i16,
+ can_fly: bool,
+ needs_driver: bool,
+ horsepower: i32,
+ top_speed: f32,
+ machine: Machine,
+}
+#[derive(Default)]
+struct Machine {
+ id: i32,
+ brand: String,
+ year: i16,
+ weight: f32,
+ length: f32,
+ width: f32,
+ height: f32,
+}
+
+#[derive(o2o)]
+#[try_map(Car, String)]
+#[try_into_existing(Car, String)]
+#[children(vehicle: Vehicle, vehicle.machine: Machine)]
+#[ghosts(vehicle.machine@id: { 321 })]
+struct CarDto {
+ number_of_doors: i8,
+
+ #[o2o(repeat)] #[child(vehicle)]
+ number_of_seats: i16,
+ can_fly: bool,
+ needs_driver: bool,
+ horsepower: i32,
+ top_speed: f32,
+ #[o2o(stop_repeat)]
+
+ #[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))] #[ghost({123})]
+ useless_param: i32,
+ useless_param_2: i32,
+ useless_param_3: i32,
+}
+
+#[test]
+fn named2named() {
+ let car = Car {
+ number_of_doors: 2,
+ vehicle: Vehicle {
+ number_of_seats: 4,
+ can_fly: false,
+ horsepower: 30,
+ needs_driver: true,
+ top_speed: 105.0,
+ machine: Machine {
+ id: 123,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960,
+ weight: 615.0,
+ length: 3360.0,
+ width: 1500.0,
+ height: 1440.0
+ }
+ }
+ };
+
+ let car_dto: CarDto = car.try_into().unwrap();
+
+ assert_eq!(2, car_dto.number_of_doors);
+ assert_eq!(4, car_dto.number_of_seats);
+ assert_eq!(false, car_dto.can_fly);
+ assert_eq!(true, car_dto.needs_driver);
+ assert_eq!(30, car_dto.horsepower);
+ assert_eq!(105.0, car_dto.top_speed);
+ assert_eq!("Trabant", car_dto.brand);
+ assert_eq!(1960, car_dto.year);
+ assert_eq!(615.0, car_dto.weight);
+ assert_eq!(3360.0, car_dto.length);
+ assert_eq!(1500.0, car_dto.width);
+ assert_eq!(1440.0, car_dto.height);
+ assert_eq!(123, car_dto.useless_param);
+ assert_eq!(123, car_dto.useless_param_2);
+ assert_eq!(123, car_dto.useless_param_3);
+}
+
+#[test]
+fn named2named_reverse() {
+ let car_dto = CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ can_fly: false,
+ needs_driver: true,
+ horsepower: 30,
+ top_speed: 105.0,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960,
+ weight: 615.0,
+ length: 3360.0,
+ width: 1500.0,
+ height: 1440.0,
+ useless_param: 123,
+ useless_param_2: 123,
+ useless_param_3: 123
+ };
+
+ let car: Car = car_dto.try_into().unwrap();
+
+ assert_eq!(2, car.number_of_doors);
+ assert_eq!(4, car.vehicle.number_of_seats);
+ assert_eq!(false, car.vehicle.can_fly);
+ assert_eq!(true, car.vehicle.needs_driver);
+ assert_eq!(30, car.vehicle.horsepower);
+ assert_eq!(105.0, car.vehicle.top_speed);
+ assert_eq!("Trabant", car.vehicle.machine.brand);
+ assert_eq!(1960, car.vehicle.machine.year);
+ assert_eq!(615.0, car.vehicle.machine.weight);
+ assert_eq!(3360.0, car.vehicle.machine.length);
+ assert_eq!(1500.0, car.vehicle.machine.width);
+ assert_eq!(1440.0, car.vehicle.machine.height);
+}
+
+#[test]
+fn named2named_ref() {
+ let car = &Car {
+ number_of_doors: 2,
+ vehicle: Vehicle {
+ number_of_seats: 4,
+ can_fly: false,
+ horsepower: 30,
+ needs_driver: true,
+ top_speed: 105.0,
+ machine: Machine {
+ id: 123,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960,
+ weight: 615.0,
+ length: 3360.0,
+ width: 1500.0,
+ height: 1440.0
+ }
+ }
+ };
+
+ let car_dto: CarDto = car.try_into().unwrap();
+
+ assert_eq!(car.number_of_doors, car_dto.number_of_doors);
+ assert_eq!(car.vehicle.number_of_seats, car_dto.number_of_seats);
+ assert_eq!(car.vehicle.can_fly, car_dto.can_fly);
+ assert_eq!(car.vehicle.needs_driver, car_dto.needs_driver);
+ assert_eq!(car.vehicle.horsepower, car_dto.horsepower);
+ assert_eq!(car.vehicle.top_speed, car_dto.top_speed);
+ assert_eq!(car.vehicle.machine.brand, car_dto.brand);
+ assert_eq!(car.vehicle.machine.year, car_dto.year);
+ assert_eq!(car.vehicle.machine.weight, car_dto.weight);
+ assert_eq!(car.vehicle.machine.length, car_dto.length);
+ assert_eq!(car.vehicle.machine.width, car_dto.width);
+ assert_eq!(car.vehicle.machine.height, car_dto.height);
+ assert_eq!(123, car_dto.useless_param);
+ assert_eq!(123, car_dto.useless_param_2);
+ assert_eq!(123, car_dto.useless_param_3);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let car_dto = &CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ can_fly: false,
+ needs_driver: true,
+ horsepower: 30,
+ top_speed: 105.0,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960,
+ weight: 615.0,
+ length: 3360.0,
+ width: 1500.0,
+ height: 1440.0,
+ useless_param: 123,
+ useless_param_2: 123,
+ useless_param_3: 123
+ };
+
+ let car: Car = car_dto.try_into().unwrap();
+
+ assert_eq!(car_dto.number_of_doors, car.number_of_doors);
+ assert_eq!(car_dto.number_of_seats, car.vehicle.number_of_seats);
+ assert_eq!(car_dto.can_fly, car.vehicle.can_fly);
+ assert_eq!(car_dto.needs_driver, car.vehicle.needs_driver);
+ assert_eq!(car_dto.horsepower, car.vehicle.horsepower);
+ assert_eq!(car_dto.top_speed, car.vehicle.top_speed);
+ assert_eq!(car_dto.brand, car.vehicle.machine.brand);
+ assert_eq!(car_dto.year, car.vehicle.machine.year);
+ assert_eq!(car_dto.weight, car.vehicle.machine.weight);
+ assert_eq!(car_dto.length, car.vehicle.machine.length);
+ assert_eq!(car_dto.width, car.vehicle.machine.width);
+ assert_eq!(car_dto.height, car.vehicle.machine.height);
+}
+
+#[test]
+fn existing_named2named() {
+ let car_dto = CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ can_fly: false,
+ needs_driver: true,
+ horsepower: 30,
+ top_speed: 105.0,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960,
+ weight: 615.0,
+ length: 3360.0,
+ width: 1500.0,
+ height: 1440.0,
+ useless_param: 123,
+ useless_param_2: 123,
+ useless_param_3: 123
+ };
+
+ let mut car: Car = Default::default();
+ car_dto.try_into_existing(&mut car).unwrap();
+
+ assert_eq!(2, car.number_of_doors);
+ assert_eq!(4, car.vehicle.number_of_seats);
+ assert_eq!(false, car.vehicle.can_fly);
+ assert_eq!(true, car.vehicle.needs_driver);
+ assert_eq!(30, car.vehicle.horsepower);
+ assert_eq!(105.0, car.vehicle.top_speed);
+ assert_eq!("Trabant", car.vehicle.machine.brand);
+ assert_eq!(1960, car.vehicle.machine.year);
+ assert_eq!(615.0, car.vehicle.machine.weight);
+ assert_eq!(3360.0, car.vehicle.machine.length);
+ assert_eq!(1500.0, car.vehicle.machine.width);
+ assert_eq!(1440.0, car.vehicle.machine.height);
+}
+
+#[test]
+fn existing_named2named_reverse() {
+ let car_dto = &CarDto {
+ number_of_doors: 2,
+ number_of_seats: 4,
+ can_fly: false,
+ needs_driver: true,
+ horsepower: 30,
+ top_speed: 105.0,
+ brand: "Trabant".try_into().unwrap(),
+ year: 1960,
+ weight: 615.0,
+ length: 3360.0,
+ width: 1500.0,
+ height: 1440.0,
+ useless_param: 123,
+ useless_param_2: 123,
+ useless_param_3: 123
+ };
+
+ let mut car: Car = Default::default();
+ car_dto.try_into_existing(&mut car).unwrap();
+
+ assert_eq!(car_dto.number_of_doors, car.number_of_doors);
+ assert_eq!(car_dto.number_of_seats, car.vehicle.number_of_seats);
+ assert_eq!(car_dto.can_fly, car.vehicle.can_fly);
+ assert_eq!(car_dto.needs_driver, car.vehicle.needs_driver);
+ assert_eq!(car_dto.horsepower, car.vehicle.horsepower);
+ assert_eq!(car_dto.top_speed, car.vehicle.top_speed);
+ assert_eq!(car_dto.brand, car.vehicle.machine.brand);
+ assert_eq!(car_dto.year, car.vehicle.machine.year);
+ assert_eq!(car_dto.weight, car.vehicle.machine.weight);
+ assert_eq!(car_dto.length, car.vehicle.machine.length);
+ assert_eq!(car_dto.width, car.vehicle.machine.width);
+ assert_eq!(car_dto.height, car.vehicle.machine.height);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/22_nameless_tuple_tests_fallible.rs b/o2o-tests/tests/22_nameless_tuple_tests_fallible.rs
new file mode 100644
index 0000000..b57742c
--- /dev/null
+++ b/o2o-tests/tests/22_nameless_tuple_tests_fallible.rs
@@ -0,0 +1,87 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(o2o)]
+#[try_map((i32, String), String)]
+#[try_into_existing((i32, String), String)]
+pub struct Entity{
+ #[map(0)]
+ int: i32,
+ #[map_owned(1)]
+ #[map_ref(1, ~.clone())]
+ string: String,
+}
+
+#[test]
+fn named2nameless() {
+ let entity = Entity {
+ int: 123,
+ string: "Test".try_into().unwrap()
+ };
+
+ let (int, string) = entity.try_into().unwrap();
+
+ assert_eq!(123, int);
+ assert_eq!("Test", string);
+}
+
+#[test]
+fn named2nameless_ref() {
+ let entity = &Entity {
+ int: 123,
+ string: "Test".try_into().unwrap()
+ };
+
+ let (int, string) = entity.try_into().unwrap();
+
+ assert_eq!(entity.int, int);
+ assert_eq!(entity.string, string);
+}
+
+#[test]
+fn named2nameless_reverse() {
+ let tpl = (123, String::from("Test"));
+
+ let entity: Entity = tpl.try_into().unwrap();
+
+ assert_eq!(123, entity.int);
+ assert_eq!("Test", entity.string);
+}
+
+#[test]
+fn named2nameless_reverse_ref() {
+ let tpl = &(123, String::from("Test"));
+
+ let entity: Entity = tpl.try_into().unwrap();
+
+ assert_eq!(tpl.0, entity.int);
+ assert_eq!(tpl.1, entity.string);
+}
+
+#[test]
+fn existing_named2nameless() {
+ let entity = Entity {
+ int: 123,
+ string: "Test".try_into().unwrap()
+ };
+
+ let mut tpl = <(i32, String)>::default();
+ entity.try_into_existing(&mut tpl).unwrap();
+
+ assert_eq!(123, tpl.0);
+ assert_eq!("Test", tpl.1);
+}
+
+#[test]
+fn existing_named2nameless_ref() {
+ let entity = &Entity {
+ int: 123,
+ string: "Test".try_into().unwrap()
+ };
+
+ let mut tpl = <(i32, String)>::default();
+ entity.try_into_existing(&mut tpl).unwrap();
+
+ assert_eq!(entity.int, tpl.0);
+ assert_eq!(entity.string, tpl.1);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/23_pre_init_tests_fallible.rs b/o2o-tests/tests/23_pre_init_tests_fallible.rs
new file mode 100644
index 0000000..781144f
--- /dev/null
+++ b/o2o-tests/tests/23_pre_init_tests_fallible.rs
@@ -0,0 +1,107 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Person {
+ age: i8,
+ first_name: String,
+ last_name: String
+}
+
+#[derive(o2o)]
+#[o2o(try_from_owned(Person, String| vars(first_name: {@.first_name}, last_name: {@.last_name})))]
+#[try_from_ref(Person, String| vars(first_name: {@.first_name.clone()}, last_name: {@.last_name.clone()}))]
+#[try_into(Person, String| vars(first: {"John"}, last: {"Doe"}))]
+#[try_into_existing(Person, String| vars(first: {"John"}, last: {"Doe"}))]
+#[ghosts(first_name: {first.try_into().unwrap()}, last_name: {last.try_into().unwrap()})]
+struct PersonDto {
+ age: i8,
+ #[ghost({format!("{} {}", first_name, last_name)})]
+ full_name: String
+}
+
+#[test]
+fn named2named() {
+ let person = Person {
+ age: 42,
+ first_name: "Dohn".try_into().unwrap(),
+ last_name: "Joe".try_into().unwrap()
+ };
+
+ let dto: PersonDto = person.try_into().unwrap();
+
+ assert_eq!(42, dto.age);
+ assert_eq!("Dohn Joe", dto.full_name);
+}
+
+#[test]
+fn named2named_ref() {
+ let person = &Person {
+ age: 42,
+ first_name: "Dohn".try_into().unwrap(),
+ last_name: "Joe".try_into().unwrap()
+ };
+
+ let dto: PersonDto = person.try_into().unwrap();
+
+ assert_eq!(person.age, dto.age);
+ assert_eq!(format!("{} {}", person.first_name, person.last_name), dto.full_name);
+}
+
+#[test]
+fn named2named_reverse() {
+ let dto = PersonDto {
+ age: 42,
+ full_name: "Test".try_into().unwrap()
+ };
+
+ let person: Person = dto.try_into().unwrap();
+
+ assert_eq!(42, person.age);
+ assert_eq!("John", person.first_name);
+ assert_eq!("Doe", person.last_name);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let dto = &PersonDto {
+ age: 42,
+ full_name: "Test".try_into().unwrap()
+ };
+
+ let person: Person = dto.try_into().unwrap();
+
+ assert_eq!(dto.age, person.age);
+ assert_eq!("John", person.first_name);
+ assert_eq!("Doe", person.last_name);
+}
+
+#[test]
+fn existing_named2named() {
+ let dto = PersonDto {
+ age: 42,
+ full_name: "Test".try_into().unwrap()
+ };
+
+ let mut person: Person = Default::default();
+ dto.try_into_existing(&mut person).unwrap();
+
+ assert_eq!(42, person.age);
+ assert_eq!("John", person.first_name);
+ assert_eq!("Doe", person.last_name);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let dto = &PersonDto {
+ age: 42,
+ full_name: "Test".try_into().unwrap()
+ };
+
+ let mut person: Person = Default::default();
+ dto.try_into_existing(&mut person).unwrap();
+
+ assert_eq!(42, person.age);
+ assert_eq!("John", person.first_name);
+ assert_eq!("Doe", person.last_name);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/24_quick_return_tests_fallible.rs b/o2o-tests/tests/24_quick_return_tests_fallible.rs
new file mode 100644
index 0000000..bc67ea5
--- /dev/null
+++ b/o2o-tests/tests/24_quick_return_tests_fallible.rs
@@ -0,0 +1,108 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(o2o)]
+#[o2o(owned_try_into(i32, String| vars(hrs: {@.hours as i32}, mns: {@.minutes as i32}, scs: {@.seconds as i32}),
+ return Ok(hrs * 3600 + mns * 60 + scs)))]
+struct Time {
+ hours: i8,
+ minutes: i8,
+ seconds: i8,
+}
+
+#[derive(o2o)]
+#[try_from(Time, String| vars(hrs: {@.hours as i32}, mns: {@.minutes as i32}, scs: {@.seconds as i32}),
+ return { Ok(TotalTime {total_seconds: hrs * 3600 + mns * 60 + scs}) })]
+#[try_into(String, String| return Ok(@.total_seconds.to_string()))]
+#[try_into_existing(String, String| return @.total_seconds.to_string())]
+struct TotalTime {
+ total_seconds: i32
+}
+
+#[test]
+fn time2i() {
+ let time = Time {
+ hours: 2,
+ minutes: 10,
+ seconds: 15
+ };
+
+ let i: i32 = time.try_into().unwrap();
+
+ assert_eq!(7815, i);
+}
+
+#[test]
+fn named2named() {
+ let time = Time {
+ hours: 2,
+ minutes: 10,
+ seconds: 15
+ };
+
+ let total: TotalTime = time.try_into().unwrap();
+
+ assert_eq!(7815, total.total_seconds);
+}
+
+#[test]
+fn named2named_ref() {
+ let time = &Time {
+ hours: 2,
+ minutes: 10,
+ seconds: 15
+ };
+
+ let total: TotalTime = time.try_into().unwrap();
+
+ let hrs = time.hours as i32;
+ let mns = time.minutes as i32;
+ let scs = time.seconds as i32;
+ assert_eq!(hrs*3600+mns*60+scs, total.total_seconds);
+}
+
+#[test]
+fn time2string() {
+ let total_time = TotalTime {
+ total_seconds: 123
+ };
+
+ let str: String = total_time.try_into().unwrap();
+
+ assert_eq!("123", str);
+}
+
+#[test]
+fn time2string_ref() {
+ let total_time = &TotalTime {
+ total_seconds: 123
+ };
+
+ let str: String = total_time.try_into().unwrap();
+
+ assert_eq!("123", str);
+}
+
+#[test]
+fn existing_time2string() {
+ let total_time = TotalTime {
+ total_seconds: 123
+ };
+
+ let mut str = String::new();
+ total_time.try_into_existing(&mut str).unwrap();
+
+ assert_eq!("123", str);
+}
+
+#[test]
+fn existing_time2string_ref() {
+ let total_time = &TotalTime {
+ total_seconds: 123
+ };
+
+ let mut str = String::new();
+ total_time.try_into_existing(&mut str).unwrap();
+
+ assert_eq!("123", str);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/25_update_struct_tests_fallible.rs b/o2o-tests/tests/25_update_struct_tests_fallible.rs
new file mode 100644
index 0000000..607edaa
--- /dev/null
+++ b/o2o-tests/tests/25_update_struct_tests_fallible.rs
@@ -0,0 +1,77 @@
+use o2o::o2o;
+
+struct Entity {
+ some_int: i32,
+ some_float: f32
+}
+
+impl Default for Entity {
+ fn default() -> Self {
+ Self { some_int: 0, some_float: 321.0 }
+ }
+}
+
+#[derive(o2o)]
+#[try_into(Entity, String| ..Default::default())]
+#[try_from(Entity, String| ..get_default())]
+struct EntityDto {
+ some_int: i32,
+ #[ghost]
+ some_string: String
+}
+
+fn get_default() -> EntityDto {
+ EntityDto { some_int: 0, some_string: "test".try_into().unwrap() }
+}
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ some_int: 123,
+ some_string: "321".try_into().unwrap()
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(321.0, entity.some_float);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ some_int: 123,
+ some_float: 654.0
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!("test", dto.some_string);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ some_string: "321".try_into().unwrap()
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(321.0, entity.some_float);
+}
+
+#[test]
+fn named2named_ref_reverse() {
+ let entity = &Entity {
+ some_int: 123,
+ some_float: 654.0
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!("test", dto.some_string);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/26_at_and_tilde_inside_inline_expr_tests_fallible.rs b/o2o-tests/tests/26_at_and_tilde_inside_inline_expr_tests_fallible.rs
new file mode 100644
index 0000000..eb5dc38
--- /dev/null
+++ b/o2o-tests/tests/26_at_and_tilde_inside_inline_expr_tests_fallible.rs
@@ -0,0 +1,394 @@
+use o2o::{o2o, traits::TryIntoExisting};
+
+#[derive(Default)]
+struct Entity {
+ some_int: i32,
+ another_int: i16,
+ some_float: f32,
+ another_float: f64,
+ extra_float: f32,
+ child: Child
+}
+
+#[derive(Default)]
+struct Child {
+ child_float: f32
+}
+
+fn another_int_to_string(e: &Entity) -> String {
+ e.another_int.to_string()
+}
+
+fn another_int_string_to_int(f: &EntityDto) -> i16 {
+ f.another_int_string.parse().unwrap()
+}
+
+fn unnamed_another_int_string_to_int(f: &UnnamedEntityDto) -> i16 {
+ f.1.parse().unwrap()
+}
+
+fn float_to_string(f: f32) -> String {
+ f.to_string()
+}
+
+fn string_to_float(f: String) -> f32 {
+ f.parse().unwrap()
+}
+
+fn float64_to_string(f: f64) -> String {
+ f.to_string()
+}
+
+fn string_to_float64(f: String) -> f64 {
+ f.parse().unwrap()
+}
+
+fn extra_string(e: &Entity) -> String {
+ e.extra_float.to_string()
+}
+
+fn extra_float(e: &EntityDto) -> f32 {
+ e.extra_string.parse().unwrap()
+}
+
+fn extra_float_2(e: &UnnamedEntityDto) -> f32 {
+ e.4.parse().unwrap()
+}
+
+#[derive(o2o)]
+#[try_map(Entity, String)]
+#[try_into_existing(Entity, String)]
+#[children(child: Child)]
+#[ghosts(extra_float: { extra_float(&@) })]
+struct EntityDto {
+ some_int: i32,
+
+ #[o2o(
+ from_ref({ another_int_to_string(@) } ),
+ from_owned({ another_int_to_string(&@) } ),
+ ref_into(another_int, another_int_string_to_int(@)),
+ owned_into(another_int, another_int_string_to_int(&@))
+ )]
+ another_int_string: String,
+
+ #[o2o(from({ float_to_string(~) } ))]
+ #[o2o(ref_into({ string_to_float(~.clone()) } ))]
+ #[o2o(owned_into({ string_to_float(~.clone()) } ))]
+ some_float: String,
+
+ #[from(another_float, { float64_to_string(~) } )]
+ #[ref_into(another_float, { string_to_float64(~.clone()) } )]
+ #[owned_into(another_float, { string_to_float64(~.clone()) } )]
+ another_float_string: String,
+
+ #[ghost(extra_string(&@))]
+ extra_string: String,
+
+ #[child(child)]
+ #[from(child_float, { float_to_string(~) })]
+ #[into(child_float, { string_to_float(~.clone()) })]
+ child_float_string: String,
+}
+
+#[derive(o2o)]
+#[try_map(Entity as {}, String)]
+#[try_into_existing(Entity as {}, String)]
+#[children(child: Child as {})]
+#[ghosts(extra_float: { extra_float_2(&@) })]
+struct UnnamedEntityDto (
+ #[map(Entity| some_int)]
+ i32,
+
+ #[o2o(
+ from_ref(another_int_to_string(@)),
+ from_owned(another_int_to_string(&@)),
+ ref_into(another_int, { unnamed_another_int_string_to_int(@) } ),
+ owned_into(another_int, { unnamed_another_int_string_to_int(&@) } )
+ )]
+ String,
+
+ #[o2o(from(some_float, { float_to_string(~) } ))]
+ #[o2o(ref_into(some_float, { string_to_float(~.clone()) } ))]
+ #[o2o(owned_into(some_float, { string_to_float(~.clone()) } ))]
+ String,
+
+ #[from(another_float, { float64_to_string(~) } )]
+ #[ref_into(another_float, { string_to_float64(~.clone()) } )]
+ #[owned_into(another_float, { string_to_float64(~.clone()) } )]
+ String,
+
+ #[ghost({ extra_string(&@) })]
+ String,
+
+ #[child(child)]
+ #[from(child_float, { float_to_string(~) })]
+ #[into(child_float, { string_to_float(~.clone()) })]
+ String,
+);
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int_string: "456".into(),
+ some_float: "789".into(),
+ another_float_string: "987".into(),
+ extra_string: "654".into(),
+ child_float_string: "321".into()
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ some_int: 123,
+ another_int: 456,
+ some_float: 789.0,
+ another_float: 987.0,
+ extra_float: 654.0,
+ child: Child {
+ child_float: 321.0
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!("456", dto.another_int_string);
+ assert_eq!("789", dto.some_float);
+ assert_eq!("987", dto.another_float_string);
+ assert_eq!("654", dto.extra_string);
+ assert_eq!("321", dto.child_float_string);
+
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int_string: "456".into(),
+ some_float: "789".into(),
+ another_float_string: "987".into(),
+ extra_string: "654".into(),
+ child_float_string: "321".into()
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
+
+#[test]
+fn named2named_ref_reverse() {
+ let entity = &Entity {
+ some_int: 123,
+ another_int: 456,
+ some_float: 789.0,
+ another_float: 987.0,
+ extra_float: 654.0,
+ child: Child {
+ child_float: 321.0
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!("456", dto.another_int_string);
+ assert_eq!("789", dto.some_float);
+ assert_eq!("987", dto.another_float_string);
+ assert_eq!("654", dto.extra_string);
+ assert_eq!("321", dto.child_float_string);
+
+}
+
+#[test]
+fn existing_named2named() {
+ let dto = EntityDto {
+ some_int: 123,
+ another_int_string: "456".into(),
+ some_float: "789".into(),
+ another_float_string: "987".into(),
+ extra_string: "654".into(),
+ child_float_string: "321".into()
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let dto = &EntityDto {
+ some_int: 123,
+ another_int_string: "456".into(),
+ some_float: "789".into(),
+ another_float_string: "987".into(),
+ extra_string: "654".into(),
+ child_float_string: "321".into()
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
+
+#[test]
+fn unnamed2named() {
+ let dto = UnnamedEntityDto (
+ 123,
+ "456".into(),
+ "789".into(),
+ "987".into(),
+ "654".into(),
+ "321".into()
+ );
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
+
+#[test]
+fn unnamed2named_reverse() {
+ let entity = Entity {
+ some_int: 123,
+ another_int: 456,
+ some_float: 789.0,
+ another_float: 987.0,
+ extra_float: 654.0,
+ child: Child {
+ child_float: 321.0
+ }
+ };
+
+ let dto: UnnamedEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!("456", dto.1);
+ assert_eq!("789", dto.2);
+ assert_eq!("987", dto.3);
+ assert_eq!("654", dto.4);
+ assert_eq!("321", dto.5);
+}
+
+#[test]
+fn unnamed2named_ref() {
+ let dto = &UnnamedEntityDto (
+ 123,
+ "456".into(),
+ "789".into(),
+ "987".into(),
+ "654".into(),
+ "321".into()
+ );
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
+
+#[test]
+fn unnamed2named_ref_reverse() {
+ let entity = &Entity {
+ some_int: 123,
+ another_int: 456,
+ some_float: 789.0,
+ another_float: 987.0,
+ extra_float: 654.0,
+ child: Child {
+ child_float: 321.0
+ }
+ };
+
+ let dto: UnnamedEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!("456", dto.1);
+ assert_eq!("789", dto.2);
+ assert_eq!("987", dto.3);
+ assert_eq!("654", dto.4);
+ assert_eq!("321", dto.5);
+
+}
+
+#[test]
+fn existing_unnamed2named() {
+ let dto = UnnamedEntityDto (
+ 123,
+ "456".into(),
+ "789".into(),
+ "987".into(),
+ "654".into(),
+ "321".into()
+ );
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
+
+#[test]
+fn existing_unnamed2named_ref() {
+ let dto = &UnnamedEntityDto (
+ 123,
+ "456".into(),
+ "789".into(),
+ "987".into(),
+ "654".into(),
+ "321".into()
+ );
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.some_int);
+ assert_eq!(456, entity.another_int);
+ assert_eq!(789.0, entity.some_float);
+ assert_eq!(987.0, entity.another_float);
+ assert_eq!(654.0, entity.extra_float);
+ assert_eq!(321.0, entity.child.child_float);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/27_enum_bare_top_level_attr_tests_fallible.rs b/o2o-tests/tests/27_enum_bare_top_level_attr_tests_fallible.rs
new file mode 100644
index 0000000..73e40bf
--- /dev/null
+++ b/o2o-tests/tests/27_enum_bare_top_level_attr_tests_fallible.rs
@@ -0,0 +1,61 @@
+#[derive(PartialEq, Eq, Clone)]
+enum Enum {
+ Item1,
+ Item2,
+}
+
+#[derive(PartialEq, Eq, Clone, o2o::o2o)]
+#[map(Enum)]
+enum EnumDto {
+ Item1,
+ Item2,
+}
+
+#[derive(PartialEq, Eq)]
+enum EnumWithData {
+ Item1(i32, i16),
+ Item2 { str: String, i: i32 },
+}
+
+#[derive(Clone, PartialEq, Eq, o2o::o2o)]
+#[try_map_owned(EnumWithData, String)]
+enum EnumWithDataDto {
+ Item1(i32, i16),
+ Item2 { str: String, i: i32 },
+}
+
+#[test]
+fn enum2enum() {
+ for data in vec![
+ (EnumDto::Item1, Enum::Item1),
+ (EnumDto::Item2, Enum::Item2)
+ ] {
+ let dto_ref = &data.0;
+ let en: Enum = dto_ref.try_into().unwrap();
+ assert!(en == data.1);
+
+ let en: Enum = data.0.clone().try_into().unwrap();
+ assert!(en == data.1);
+
+ let en_ref = &data.1;
+ let dto: EnumDto = en_ref.try_into().unwrap();
+ assert!(dto == data.0);
+
+ let dto: EnumDto = data.1.try_into().unwrap();
+ assert!(dto == data.0);
+ }
+}
+
+#[test]
+fn enum2enum_with_data() {
+ for data in vec![
+ (EnumWithDataDto::Item1(123, 321), EnumWithData::Item1(123, 321)),
+ (EnumWithDataDto::Item2 { str: "Test".into(), i: 654 }, EnumWithData::Item2 { str: "Test".into(), i: 654 })
+ ] {
+ let en: EnumWithData = data.0.clone().try_into().unwrap();
+ assert!(en == data.1);
+
+ let dto: EnumWithDataDto = data.1.try_into().unwrap();
+ assert!(dto == data.0);
+ }
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/28_enum_member_level_ident_only_tests_fallible.rs b/o2o-tests/tests/28_enum_member_level_ident_only_tests_fallible.rs
new file mode 100644
index 0000000..1d36bf6
--- /dev/null
+++ b/o2o-tests/tests/28_enum_member_level_ident_only_tests_fallible.rs
@@ -0,0 +1,67 @@
+#[derive(PartialEq, Eq)]
+enum Enum {
+ Item1,
+ Item2,
+}
+
+#[derive(Clone, PartialEq, Eq, o2o::o2o)]
+#[try_map(Enum, String)]
+enum EnumDto {
+ Item1,
+ #[map(Item2)]
+ Item2Dto,
+}
+
+#[derive(PartialEq, Eq)]
+enum EnumWithData {
+ Item1(i32, i16),
+ Item2 { str: String, i: i32 },
+}
+
+#[derive(Clone, PartialEq, Eq, o2o::o2o)]
+#[try_map_owned(EnumWithData, String)]
+enum EnumWithDataDto {
+ #[map(Item1)]
+ Item1Dto(i32, i16),
+ Item2 {
+ #[map(str)]
+ string: String,
+ i: i32
+ },
+}
+
+#[test]
+fn enum2enum() {
+ for data in vec![
+ (EnumDto::Item1, Enum::Item1),
+ (EnumDto::Item2Dto, Enum::Item2)
+ ] {
+ let dto_ref = &data.0;
+ let en: Enum = dto_ref.try_into().unwrap();
+ assert!(en == data.1);
+
+ let en: Enum = data.0.clone().try_into().unwrap();
+ assert!(en == data.1);
+
+ let en_ref = &data.1;
+ let dto: EnumDto = en_ref.try_into().unwrap();
+ assert!(dto == data.0);
+
+ let dto: EnumDto = data.1.try_into().unwrap();
+ assert!(dto == data.0);
+ }
+}
+
+#[test]
+fn enum2enum_with_data() {
+ for data in vec![
+ (EnumWithDataDto::Item1Dto(123, 321), EnumWithData::Item1(123, 321)),
+ (EnumWithDataDto::Item2 { string: "Test".into(), i: 654 }, EnumWithData::Item2 { str: "Test".into(), i: 654 })
+ ] {
+ let en: EnumWithData = data.0.clone().try_into().unwrap();
+ assert!(en == data.1);
+
+ let dto: EnumWithDataDto = data.1.try_into().unwrap();
+ assert!(dto == data.0);
+ }
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/29_enum_member_level_inline_expr_only_tests_fallible.rs b/o2o-tests/tests/29_enum_member_level_inline_expr_only_tests_fallible.rs
new file mode 100644
index 0000000..852a0ba
--- /dev/null
+++ b/o2o-tests/tests/29_enum_member_level_inline_expr_only_tests_fallible.rs
@@ -0,0 +1,36 @@
+#[derive(PartialEq, Eq)]
+enum EnumWithData {
+ Item1(i32, i16),
+ Item2 { str: String, i: i32 },
+}
+
+#[derive(Clone, PartialEq, Eq, o2o::o2o)]
+#[try_map_owned(EnumWithData, String)]
+enum EnumWithDataDto {
+ Item1(
+ i32,
+ #[from_owned(~ * 2)]
+ #[owned_into(~ / 2)]
+ i16
+ ),
+ Item2 {
+ str: String,
+ #[from(~.to_string())]
+ #[into(~.parse::().unwrap())]
+ i: String
+ },
+}
+
+#[test]
+fn enum2enum_with_data() {
+ for data in vec![
+ (EnumWithDataDto::Item1(123, 222), EnumWithData::Item1(123, 111)),
+ (EnumWithDataDto::Item2 { str: "Test".into(), i: "654".into() }, EnumWithData::Item2 { str: "Test".into(), i: 654 })
+ ] {
+ let en: EnumWithData = data.0.clone().try_into().unwrap();
+ assert!(en == data.1);
+
+ let dto: EnumWithDataDto = data.1.try_into().unwrap();
+ assert!(dto == data.0);
+ }
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/2_field_level_ident_only_attr_tests_fallible.rs b/o2o-tests/tests/2_field_level_ident_only_attr_tests_fallible.rs
new file mode 100644
index 0000000..2bb8ec0
--- /dev/null
+++ b/o2o-tests/tests/2_field_level_ident_only_attr_tests_fallible.rs
@@ -0,0 +1,341 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct NamedStruct {
+ some_int: i32,
+ another_int: i32,
+ some_float: f32,
+}
+
+#[derive(Default)]
+struct NamedStructModel {
+ some_int: i32,
+ another_int: i32,
+ some_float_diff: f32,
+}
+
+#[derive(o2o)]
+#[try_map(NamedStruct, String)]
+#[try_map(NamedStructModel, String)]
+#[try_into_existing(NamedStruct, String)]
+#[try_into_existing(NamedStructModel, String)]
+struct NamedStructDto {
+ some_int: i32,
+ #[try_map(another_int)]
+ diff_another_int: i32,
+ #[map(NamedStruct| some_float)]
+ #[map(NamedStructModel| some_float_diff)]
+ diff_some_float: f32,
+}
+
+#[derive(Default, o2o)]
+#[o2o(try_from(NamedStruct, i32))]
+#[o2o(try_from(NamedStructModel, i32))]
+struct UnnamedStructDto(
+ #[o2o(try_map(some_int))] i32,
+ #[o2o(map(another_int))] i32,
+ #[o2o(map(NamedStruct| some_float), map(NamedStructModel| some_float_diff))] f32
+);
+
+#[derive(Default)]
+#[derive(o2o)]
+#[try_map(UnnamedStructDto, anyhow::Error)]
+#[try_into_existing(UnnamedStructDto, anyhow::Error)]
+struct NamedStruct2 {
+ #[try_map(0)] some_int: i32,
+ #[map(1)] another_int: i32,
+ #[map(2)] some_float: f32,
+}
+
+#[test]
+fn named2named() {
+ let dto = NamedStructDto {
+ some_int: 123,
+ diff_another_int: 321,
+ diff_some_float: 456.0,
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+
+ let dto = NamedStructDto {
+ some_int: 123,
+ diff_another_int: 321,
+ diff_some_float: 456.0,
+ };
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(456.0, model.some_float_diff);
+}
+
+#[test]
+fn named2named_reverse() {
+ let named = NamedStruct {
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.diff_another_int);
+ assert_eq!(456.0, dto.diff_some_float);
+
+ let model = NamedStructModel {
+ some_int: 123,
+ another_int: 321,
+ some_float_diff: 456.0
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.diff_another_int);
+ assert_eq!(456.0, dto.diff_some_float);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &NamedStructDto {
+ some_int: 123,
+ diff_another_int: 321,
+ diff_some_float: 456.0
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.diff_another_int);
+ assert_eq!(named.some_float, dto.diff_some_float);
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(named.some_int, model.some_int);
+ assert_eq!(named.another_int, model.another_int);
+ assert_eq!(named.some_float, model.some_float_diff);
+}
+
+#[test]
+fn named2named_ref_reversed() {
+ let named = &NamedStruct {
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.diff_another_int);
+ assert_eq!(named.some_float, dto.diff_some_float);
+
+ let model = &NamedStructModel {
+ some_int: 123,
+ another_int: 321,
+ some_float_diff: 456.0
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, dto.some_int);
+ assert_eq!(model.another_int, dto.diff_another_int);
+ assert_eq!(model.some_float_diff, dto.diff_some_float);
+}
+
+#[test]
+fn named2unnamed() {
+ let named = NamedStruct {
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let unnamed: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, unnamed.0);
+ assert_eq!(321, unnamed.1);
+ assert_eq!(456.0, unnamed.2);
+
+ let model = NamedStructModel {
+ some_int: 123,
+ another_int: 321,
+ some_float_diff: 456.0,
+ };
+
+ let unnamed: UnnamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(123, unnamed.0);
+ assert_eq!(321, unnamed.1);
+ assert_eq!(456.0, unnamed.2);
+}
+
+#[test]
+fn named2unnamed_ref() {
+ let named = &NamedStruct {
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let unnamed: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, unnamed.0);
+ assert_eq!(named.another_int, unnamed.1);
+ assert_eq!(named.some_float, unnamed.2);
+
+ let model = &NamedStructModel {
+ some_int: 123,
+ another_int: 321,
+ some_float_diff: 456.0,
+ };
+
+ let unnamed: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(model.some_int, unnamed.0);
+ assert_eq!(model.another_int, unnamed.1);
+ assert_eq!(model.some_float_diff, unnamed.2);
+}
+
+#[test]
+fn unnamed2named() {
+ let dto = UnnamedStructDto(123, 321, 456.0);
+
+ let named: NamedStruct2 = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+}
+
+#[test]
+fn unnamed2named_reverse() {
+ let named = NamedStruct2{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let dto: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456.0, dto.2);
+}
+
+#[test]
+fn unnamed2named_ref() {
+ let dto = &UnnamedStructDto(123, 321, 456.0);
+
+ let named: NamedStruct2 = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, named.some_int);
+ assert_eq!(dto.1, named.another_int);
+ assert_eq!(dto.2, named.some_float);
+}
+
+#[test]
+fn unnamed2named_reverse_ref() {
+ let named = &NamedStruct2{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let dto: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.0);
+ assert_eq!(named.another_int, dto.1);
+ assert_eq!(named.some_float, dto.2);
+}
+
+#[test]
+fn existing_named2named() {
+ let dto = NamedStructDto {
+ some_int: 123,
+ diff_another_int: 321,
+ diff_some_float: 456.0,
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+
+ let dto = NamedStructDto {
+ some_int: 123,
+ diff_another_int: 321,
+ diff_some_float: 456.0,
+ };
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(321, model.another_int);
+ assert_eq!(456.0, model.some_float_diff);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let dto = &NamedStructDto {
+ some_int: 123,
+ diff_another_int: 321,
+ diff_some_float: 456.0
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.diff_another_int);
+ assert_eq!(named.some_float, dto.diff_some_float);
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(named.some_int, model.some_int);
+ assert_eq!(named.another_int, model.another_int);
+ assert_eq!(named.some_float, model.some_float_diff);
+}
+
+#[test]
+fn existing_named2unnamed() {
+ let named = NamedStruct2{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let mut dto: UnnamedStructDto = Default::default();
+ named.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456.0, dto.2);
+}
+
+#[test]
+fn existing_named2unnamed_ref() {
+ let named = &NamedStruct2{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let mut dto: UnnamedStructDto = Default::default();
+ named.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(named.some_int, dto.0);
+ assert_eq!(named.another_int, dto.1);
+ assert_eq!(named.some_float, dto.2);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests_fallible.rs b/o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests_fallible.rs
new file mode 100644
index 0000000..180b685
--- /dev/null
+++ b/o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests_fallible.rs
@@ -0,0 +1,36 @@
+#[derive(PartialEq, Eq)]
+enum EnumWithData {
+ Item1(i32, i16),
+ Item2 { str: String, i: i32 },
+}
+
+#[derive(Clone, PartialEq, Eq, o2o::o2o)]
+#[try_map_owned(EnumWithData, String)]
+enum EnumWithDataDto {
+ Item1(
+ #[from(0, ~.to_string())]
+ #[into(0, ~.parse::().unwrap())]
+ String,
+ i16
+ ),
+ Item2 {
+ str: String,
+ #[from(i, ~.to_string())]
+ #[into(i, ~.parse::().unwrap())]
+ i_str: String
+ },
+}
+
+#[test]
+fn enum2enum_with_data() {
+ for data in vec![
+ (EnumWithDataDto::Item1("123".into(), 321), EnumWithData::Item1(123, 321)),
+ (EnumWithDataDto::Item2 { str: "Test".into(), i_str: "654".into() }, EnumWithData::Item2 { str: "Test".into(), i: 654 })
+ ] {
+ let en: EnumWithData = data.0.clone().try_into().unwrap();
+ assert!(en == data.1);
+
+ let dto: EnumWithDataDto = data.1.try_into().unwrap();
+ assert!(dto == data.0);
+ }
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/31_enum_literal_tests_fallible.rs b/o2o-tests/tests/31_enum_literal_tests_fallible.rs
new file mode 100644
index 0000000..00372bb
--- /dev/null
+++ b/o2o-tests/tests/31_enum_literal_tests_fallible.rs
@@ -0,0 +1,79 @@
+use test_case::test_case;
+
+#[derive(PartialEq, Debug, o2o::o2o)]
+#[try_map(i32, String| _ => panic!("Not supported"))]
+enum HttpStatus {
+ #[literal(200)]Ok,
+ #[literal(404)]NotFound,
+ #[literal(500)]InternalError
+}
+
+type StaticStr = &'static str;
+
+#[derive(PartialEq, Debug, o2o::o2o)]
+#[try_map_owned(StaticStr, String| _ => todo!())]
+enum Animal {
+ #[literal("🐶")] Dog,
+ #[literal("🐱")] Cat,
+ #[literal("🐵")] Monkey
+}
+
+#[test_case(200, HttpStatus::Ok ; "200_OK")]
+#[test_case(404, HttpStatus::NotFound ; "400_NotFound")]
+#[test_case(500, HttpStatus::InternalError ; "500_InternalError")]
+fn http_status_success(lit: i32, status: HttpStatus) {
+ let s: HttpStatus = lit.try_into().unwrap();
+ assert_eq!(status, s);
+
+ let l: i32 = status.try_into().unwrap();
+ assert_eq!(lit, l);
+}
+
+#[test_case(200, HttpStatus::Ok ; "200_OK")]
+#[test_case(404, HttpStatus::NotFound ; "400_NotFound")]
+#[test_case(500, HttpStatus::InternalError ; "500_InternalError")]
+fn http_status_ref_success(lit: i32, status: HttpStatus) {
+ let lit_ref = &lit;
+ let status_ref = &status;
+
+ let s: HttpStatus = lit_ref.try_into().unwrap();
+ assert_eq!(status, s);
+
+ let l: i32 = status_ref.try_into().unwrap();
+ assert_eq!(lit, l);
+}
+
+#[test_case("🐶", Animal::Dog ; "Dog")]
+#[test_case("🐱", Animal::Cat ; "Cat")]
+#[test_case("🐵", Animal::Monkey ; "Monkey")]
+fn animal_success(lit: &'static str, animal: Animal) {
+ let a: Animal = lit.try_into().unwrap();
+ assert_eq!(animal, a);
+
+ let l: &str = animal.try_into().unwrap();
+ assert_eq!(lit, l);
+}
+
+#[test]
+#[should_panic = "Not supported"]
+fn http_status_failure() {
+ let lit = 999;
+
+ let _ = HttpStatus::try_from(lit);
+}
+
+#[test]
+#[should_panic = "Not supported"]
+fn http_status_ref_failure() {
+ let lit = &999;
+
+ let _ = HttpStatus::try_from(lit);
+}
+
+#[test]
+#[should_panic = "not yet implemented"]
+fn animal_failure() {
+ let a = "Whale";
+
+ let _ = Animal::try_from(a);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/32_enum_pattern_tests_fallible.rs b/o2o-tests/tests/32_enum_pattern_tests_fallible.rs
new file mode 100644
index 0000000..ffa5a40
--- /dev/null
+++ b/o2o-tests/tests/32_enum_pattern_tests_fallible.rs
@@ -0,0 +1,75 @@
+use test_case::{test_case, test_matrix};
+
+#[derive(PartialEq, Debug, o2o::o2o)]
+#[try_from(i32, String| _ => panic!())]
+enum HttpStatusFamily {
+ #[pattern(100..=199)] Information,
+ #[pattern(200..=299)] Success,
+ #[pattern(300..=399)] Redirection,
+ #[pattern(400..=499)] ClientError,
+ #[pattern(500..=599)] ServerError,
+}
+
+type StaticStr = &'static str;
+
+#[derive(PartialEq, Debug, o2o::o2o)]
+#[try_from_owned(StaticStr, String| _ => todo!())]
+enum AnimalKind {
+ #[pattern("🐶" | "🐱" | "🐵")]
+ Mammal,
+
+ #[pattern("🐟")]
+ Fish,
+
+ #[pattern("🐛" | "🐜")]
+ Insect
+}
+
+#[test_matrix([101, 102], [HttpStatusFamily::Information] ; "100_Info")]
+#[test_matrix([200, 204], [HttpStatusFamily::Success] ; "200_Success")]
+#[test_matrix([301, 303], [HttpStatusFamily::Redirection] ; "300_Redirection")]
+#[test_matrix([400, 404], [HttpStatusFamily::ClientError] ; "400_ClientError")]
+#[test_matrix([500, 501], [HttpStatusFamily::ServerError] ; "500_ServerError")]
+fn http_status_success(lit: i32, status: HttpStatusFamily) {
+ let s: HttpStatusFamily = lit.try_into().unwrap();
+ assert_eq!(status, s);
+}
+
+#[test_matrix([101, 102], [HttpStatusFamily::Information] ; "100_Info")]
+#[test_matrix([200, 204], [HttpStatusFamily::Success] ; "200_Success")]
+#[test_matrix([301, 303], [HttpStatusFamily::Redirection] ; "300_Redirection")]
+#[test_matrix([400, 404], [HttpStatusFamily::ClientError] ; "400_ClientError")]
+#[test_matrix([500, 501], [HttpStatusFamily::ServerError] ; "500_ServerError")]
+fn http_status_ref_success(lit: i32, status: HttpStatusFamily) {
+ let lit_ref = &lit;
+
+ let s: HttpStatusFamily = lit_ref.try_into().unwrap();
+ assert_eq!(status, s);
+}
+
+#[test_case("🐶", AnimalKind::Mammal ; "Dog")]
+#[test_case("🐱", AnimalKind::Mammal ; "Cat")]
+#[test_case("🐵", AnimalKind::Mammal ; "Monkey")]
+#[test_case("🐟", AnimalKind::Fish ; "Fish")]
+#[test_case("🐛", AnimalKind::Insect ; "Caterpillar")]
+#[test_case("🐜", AnimalKind::Insect ; "Bug")]
+fn animal_success(lit: &'static str, animal: AnimalKind) {
+ let a: AnimalKind = lit.try_into().unwrap();
+ assert_eq!(animal, a);
+}
+
+#[test]
+#[should_panic = "explicit panic"]
+fn http_status_ref_failure() {
+ let lit = &999;
+
+ let _ = HttpStatusFamily::try_from(lit);
+}
+
+#[test]
+#[should_panic = "not yet implemented"]
+fn animal_failure() {
+ let a = "Whale";
+
+ let _ = AnimalKind::try_from(a);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/33_enum_literal_pattern_tests_fallible.rs b/o2o-tests/tests/33_enum_literal_pattern_tests_fallible.rs
new file mode 100644
index 0000000..fc05475
--- /dev/null
+++ b/o2o-tests/tests/33_enum_literal_pattern_tests_fallible.rs
@@ -0,0 +1,67 @@
+use test_case::test_case;
+
+#[derive(PartialEq, Debug, o2o::o2o)]
+#[try_map(i32, String)]
+enum HttpStatus {
+ #[literal(200)] Ok,
+ #[literal(404)] NotFound,
+ #[literal(500)] InternalError,
+ #[pattern(_)]
+ #[owned_into({f0})]
+ #[ref_into({*f0})]
+ Other(
+ #[from_owned(@)]
+ #[from_ref(*@)]
+ i32
+ )
+}
+
+type StaticStr = &'static str;
+
+#[derive(PartialEq, Debug, o2o::o2o)]
+#[try_map_owned(StaticStr, String)]
+enum Animal {
+ #[literal("🐶")] Dog,
+ #[literal("🐱")] Cat,
+ #[literal("🐵")] Monkey,
+ #[pattern(_)] #[into({name})] Other{ #[from(@)] name: StaticStr }
+}
+
+#[test_case(200, HttpStatus::Ok ; "200_OK")]
+#[test_case(404, HttpStatus::NotFound ; "400_NotFound")]
+#[test_case(500, HttpStatus::InternalError ; "500_InternalError")]
+#[test_case(204, HttpStatus::Other(204) ; "204_Other")]
+fn http_status_success(lit: i32, status: HttpStatus) {
+ let s: HttpStatus = lit.try_into().unwrap();
+ assert_eq!(status, s);
+
+ let l: i32 = status.try_into().unwrap();
+ assert_eq!(lit, l);
+}
+
+#[test_case(200, HttpStatus::Ok ; "200_OK")]
+#[test_case(404, HttpStatus::NotFound ; "400_NotFound")]
+#[test_case(500, HttpStatus::InternalError ; "500_InternalError")]
+#[test_case(204, HttpStatus::Other(204) ; "204_Other")]
+fn http_status_ref_success(lit: i32, status: HttpStatus) {
+ let lit_ref = &lit;
+ let status_ref = &status;
+
+ let s: HttpStatus = lit_ref.try_into().unwrap();
+ assert_eq!(status, s);
+
+ let l: i32 = status_ref.try_into().unwrap();
+ assert_eq!(lit, l);
+}
+
+#[test_case("🐶", Animal::Dog ; "Dog")]
+#[test_case("🐱", Animal::Cat ; "Cat")]
+#[test_case("🐵", Animal::Monkey ; "Monkey")]
+#[test_case("Whale", Animal::Other { name: "Whale" } ; "Whale")]
+fn animal_success(lit: &'static str, animal: Animal) {
+ let a: Animal = lit.try_into().unwrap();
+ assert_eq!(animal, a);
+
+ let l: &str = animal.try_into().unwrap();
+ assert_eq!(lit, l);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/3_field_level_inline_at_expr_only_attr_tests_fallible.rs b/o2o-tests/tests/3_field_level_inline_at_expr_only_attr_tests_fallible.rs
new file mode 100644
index 0000000..8ba14d1
--- /dev/null
+++ b/o2o-tests/tests/3_field_level_inline_at_expr_only_attr_tests_fallible.rs
@@ -0,0 +1,515 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct UnnamedStructDto(i32, i32, f32);
+
+#[derive(Default)]
+#[derive(o2o)]
+// Map works here as long as Rust allows instantiation like TupleStruct { 0: ..., 1: ..., ...}
+#[try_map(UnnamedStructDto, anyhow::Error)]
+#[try_into_existing(UnnamedStructDto, anyhow::Error)]
+struct NamedStruct {
+ #[try_map(0)] some_int: i32,
+ #[map(1)] another_int: i32,
+ #[map(2)] some_float: f32,
+}
+
+#[derive(Default)]
+struct NamedStructModel {
+ some_int: i32,
+ another_int: i8,
+ some_float: f32,
+}
+
+#[derive(o2o)]
+#[o2o(
+ try_map(NamedStruct, anyhow::Error),
+ try_map(NamedStructModel, anyhow::Error),
+ try_into_existing(NamedStruct, anyhow::Error),
+ try_into_existing(NamedStructModel, anyhow::Error),
+)]
+struct NamedStructDto {
+ some_int: i32,
+ #[o2o(
+ try_from(NamedStruct| @.another_int as i16),
+ try_into(NamedStruct| @.another_int as i32),
+ from(NamedStructModel| @.another_int as i16),
+ into(NamedStructModel| @.another_int as i8),
+ )]
+ another_int: i16,
+ #[o2o(from(@.some_float as f64))]
+ #[o2o(into(@.some_float as f32))]
+ some_float: f64,
+}
+
+struct Parent {
+ child: Child,
+ parent_int: i32,
+}
+
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map_owned(Parent, anyhow::Error)]
+struct ParentDto {
+ #[map_owned(@.child.try_into()?)]
+ child: ChildDto,
+ parent_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(Child, anyhow::Error)]
+struct ChildDto {
+ child_int: i32,
+ #[map(another_child_int)]
+ diff_another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_from(Parent, anyhow::Error)]
+struct ParentDto2 {
+ parent_int: i32,
+ #[try_from(@.child.child_int)] child_int: i32,
+ #[from(@.child.another_child_int)] another_child_int: i32,
+}
+
+struct Person {
+ name: String,
+ pets: Vec,
+}
+
+struct Pet {
+ age: i8,
+ nickname: String
+}
+
+#[derive(o2o)]
+#[try_map(Person, anyhow::Error)]
+struct PersonDto {
+ #[map(@.name.clone())]
+ name: String,
+ #[map(@.pets.iter().map(|p|p.try_into().unwrap()).collect())]
+ pets: Vec,
+}
+
+#[derive(o2o)]
+#[try_map(Pet, anyhow::Error)]
+struct PetDto {
+ age: i8,
+ #[map(@.nickname.clone())]
+ nickname: String,
+}
+
+#[test]
+fn unnamed2named() {
+ let dto = UnnamedStructDto(123, 321, 456.0);
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+}
+
+#[test]
+fn named2unnamed() {
+ let named = NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let dto: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456.0, dto.2);
+}
+
+#[test]
+fn unnamed2named_ref() {
+ let dto = &UnnamedStructDto(123, 321, 456.0);
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, named.some_int);
+ assert_eq!(dto.1, named.another_int);
+ assert_eq!(dto.2, named.some_float);
+}
+
+#[test]
+fn named2unnamed_ref() {
+ let named = &NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let dto: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.0);
+ assert_eq!(named.another_int, dto.1);
+ assert_eq!(named.some_float, dto.2);
+}
+
+#[test]
+fn named2named_uneven() {
+ let p = Parent {
+ parent_int: 123,
+ child: Child {
+ child_int: 321,
+ another_child_int: 456,
+ },
+ };
+
+ let dto: ParentDto2 = p.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.child_int);
+ assert_eq!(456, dto.another_child_int);
+}
+
+#[test]
+fn named2named_uneven_ref() {
+ let parent = &Parent {
+ parent_int: 123,
+ child: Child {
+ child_int: 321,
+ another_child_int: 456,
+ },
+ };
+
+ let dto: ParentDto2 = parent.try_into().unwrap();
+
+ assert_eq!(parent.parent_int, dto.parent_int);
+ assert_eq!(parent.child.child_int, dto.child_int);
+ assert_eq!(parent.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn named2named_different_types(){
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.another_int);
+ assert_eq!(456.0, model.some_float);
+}
+
+#[test]
+fn named2named_different_types_reverse(){
+ let named = NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.another_int);
+ assert_eq!(456.0, dto.some_float);
+
+ let model = NamedStructModel{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(127, dto.another_int);
+ assert_eq!(456.0, dto.some_float);
+}
+
+#[test]
+fn named2named_different_types_ref(){
+ let dto = &NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int, named.another_int as i16);
+ assert_eq!(dto.some_float, named.some_float as f64);
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, model.some_int);
+ assert_eq!(dto.another_int, model.another_int as i16);
+ assert_eq!(dto.some_float, model.some_float as f64);
+}
+
+#[test]
+fn named2named_different_types_reverse_ref(){
+ let named = &NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.another_int as i32);
+ assert_eq!(named.some_float, dto.some_float as f32);
+
+ let model = &NamedStructModel{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, dto.some_int);
+ assert_eq!(model.another_int, dto.another_int as i8);
+ assert_eq!(model.some_float, dto.some_float as f32);
+}
+
+#[test]
+fn named2named_child() {
+ let p = Parent {
+ parent_int: 123,
+ child: Child {
+ child_int: 321,
+ another_child_int: 456,
+ },
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.child.child_int);
+ assert_eq!(456, dto.child.diff_another_child_int);
+}
+
+#[test]
+fn named2named_child_reverse() {
+ let dto = ParentDto {
+ parent_int: 123,
+ child: ChildDto {
+ child_int: 321,
+ diff_another_child_int: 456,
+ },
+ };
+
+ let parent: Parent = dto.try_into().unwrap();
+
+ assert_eq!(123, parent.parent_int);
+ assert_eq!(321, parent.child.child_int);
+ assert_eq!(456, parent.child.another_child_int);
+}
+
+#[test]
+fn named2named_children() {
+ let dto = PersonDto {
+ name: String::from("John"),
+ pets: vec![
+ PetDto {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ PetDto {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let p: Person = dto.try_into().unwrap();
+
+ assert_eq!("John", p.name);
+ assert_eq!(2, p.pets.len());
+ assert_eq!(5, p.pets[0].age);
+ assert_eq!("Mr. Dog", p.pets[0].nickname);
+ assert_eq!(10, p.pets[1].age);
+ assert_eq!("Mr. Cat", p.pets[1].nickname);
+}
+
+#[test]
+fn named2named_children_reverse() {
+ let p = Person {
+ name: String::from("John"),
+ pets: vec![
+ Pet {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ Pet {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let dto: PersonDto = p.try_into().unwrap();
+
+ assert_eq!("John", dto.name);
+ assert_eq!(2, dto.pets.len());
+ assert_eq!(5, dto.pets[0].age);
+ assert_eq!("Mr. Dog", dto.pets[0].nickname);
+ assert_eq!(10, dto.pets[1].age);
+ assert_eq!("Mr. Cat", dto.pets[1].nickname);
+}
+
+#[test]
+fn named2named_children_ref() {
+ let dto = &PersonDto {
+ name: String::from("John"),
+ pets: vec![
+ PetDto {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ PetDto {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let p: Person = dto.try_into().unwrap();
+
+ assert_eq!(dto.name, p.name);
+ assert_eq!(2, p.pets.len());
+ assert_eq!(dto.pets[0].age, p.pets[0].age);
+ assert_eq!(dto.pets[0].nickname, p.pets[0].nickname);
+ assert_eq!(dto.pets[1].age, p.pets[1].age);
+ assert_eq!(dto.pets[1].nickname, p.pets[1].nickname);
+}
+
+#[test]
+fn named2named_children_ref_reversed() {
+ let p = &Person {
+ name: String::from("John"),
+ pets: vec![
+ Pet {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ Pet {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let dto: PersonDto = p.try_into().unwrap();
+
+ assert_eq!(dto.name, p.name);
+ assert_eq!(2, p.pets.len());
+ assert_eq!(dto.pets[0].age, p.pets[0].age);
+ assert_eq!(dto.pets[0].nickname, p.pets[0].nickname);
+ assert_eq!(dto.pets[1].age, p.pets[1].age);
+ assert_eq!(dto.pets[1].nickname, p.pets[1].nickname);
+}
+
+#[test]
+fn existing_named2unnamed() {
+ let named = NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let mut dto: UnnamedStructDto = Default::default();
+ named.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456.0, dto.2);
+}
+
+#[test]
+fn existing_named2unnamed_ref() {
+ let named = &NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0
+ };
+
+ let mut dto: UnnamedStructDto = Default::default();
+ named.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(named.some_int, dto.0);
+ assert_eq!(named.another_int, dto.1);
+ assert_eq!(named.some_float, dto.2);
+}
+
+#[test]
+fn existing_named2named_different_types(){
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.another_int);
+ assert_eq!(456.0, model.some_float);
+}
+
+#[test]
+fn existing_named2named_different_types_ref(){
+ let dto = &NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int, named.another_int as i16);
+ assert_eq!(dto.some_float, named.some_float as f64);
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.some_int, model.some_int);
+ assert_eq!(dto.another_int, model.another_int as i16);
+ assert_eq!(dto.some_float, model.some_float as f64);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/4_field_level_inline_tilde_expr_only_attr_tests_fallible.rs b/o2o-tests/tests/4_field_level_inline_tilde_expr_only_attr_tests_fallible.rs
new file mode 100644
index 0000000..431de35
--- /dev/null
+++ b/o2o-tests/tests/4_field_level_inline_tilde_expr_only_attr_tests_fallible.rs
@@ -0,0 +1,382 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct NamedStruct {
+ some_int: i32,
+ another_int: i32,
+ some_float: f32,
+}
+
+#[derive(Default)]
+struct NamedStructModel {
+ some_int: i32,
+ another_int: i8,
+ some_float: f32,
+}
+
+#[derive(o2o)]
+#[o2o(
+ try_map(NamedStruct, String),
+ try_map(NamedStructModel, String),
+ try_into_existing(NamedStruct, String),
+ try_into_existing(NamedStructModel, String),
+)]
+struct NamedStructDto {
+ some_int: i32,
+ #[o2o(
+ from(NamedStruct| ~ as i16),
+ into(NamedStruct| ~ as i32),
+ from(NamedStructModel| ~ as i16),
+ into(NamedStructModel| ~ as i8),
+ )]
+ another_int: i16,
+ #[o2o(from(~ as f64))]
+ #[o2o(into(~ as f32))]
+ some_float: f64,
+}
+
+struct Parent {
+ child: Child,
+ parent_int: i32,
+}
+
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map_owned(Parent, anyhow::Error)]
+struct ParentDto {
+ #[map_owned(~.try_into()?)]
+ child: ChildDto,
+ parent_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(Child, anyhow::Error)]
+struct ChildDto {
+ child_int: i32,
+ #[map(another_child_int)]
+ diff_another_child_int: i32,
+}
+
+struct Person {
+ name: String,
+ pets: Vec,
+}
+
+struct Pet {
+ age: i8,
+ nickname: String
+}
+
+#[derive(o2o)]
+#[try_map(Person, anyhow::Error)]
+struct PersonDto {
+ #[map(~.clone())]
+ name: String,
+ #[map(~.iter().map(|p|p.try_into().unwrap()).collect())]
+ pets: Vec,
+}
+
+#[derive(o2o)]
+#[try_map(Pet, anyhow::Error)]
+struct PetDto {
+ age: i8,
+ #[map(~.clone())]
+ nickname: String,
+}
+
+#[test]
+fn named2named_different_types(){
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.another_int);
+ assert_eq!(456.0, model.some_float);
+}
+
+#[test]
+fn named2named_different_types_reverse(){
+ let named = NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(321, dto.another_int);
+ assert_eq!(456.0, dto.some_float);
+
+ let model = NamedStructModel{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(127, dto.another_int);
+ assert_eq!(456.0, dto.some_float);
+}
+
+#[test]
+fn named2named_different_types_ref(){
+ let dto = &NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int, named.another_int as i16);
+ assert_eq!(dto.some_float, named.some_float as f64);
+
+ let model: NamedStructModel = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, model.some_int);
+ assert_eq!(dto.another_int, model.another_int as i16);
+ assert_eq!(dto.some_float, model.some_float as f64);
+}
+
+#[test]
+fn named2named_different_types_reverse_ref(){
+ let named = &NamedStruct{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.some_int);
+ assert_eq!(named.another_int, dto.another_int as i32);
+ assert_eq!(named.some_float, dto.some_float as f32);
+
+ let model = &NamedStructModel{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let dto: NamedStructDto = model.try_into().unwrap();
+
+ assert_eq!(model.some_int, dto.some_int);
+ assert_eq!(model.another_int, dto.another_int as i8);
+ assert_eq!(model.some_float, dto.some_float as f32);
+}
+
+#[test]
+fn named2named_child() {
+ let p = Parent {
+ parent_int: 123,
+ child: Child {
+ child_int: 321,
+ another_child_int: 456,
+ },
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.child.child_int);
+ assert_eq!(456, dto.child.diff_another_child_int);
+}
+
+#[test]
+fn named2named_child_reverse() {
+ let dto = ParentDto {
+ parent_int: 123,
+ child: ChildDto {
+ child_int: 321,
+ diff_another_child_int: 456,
+ },
+ };
+
+ let parent: Parent = dto.try_into().unwrap();
+
+ assert_eq!(123, parent.parent_int);
+ assert_eq!(321, parent.child.child_int);
+ assert_eq!(456, parent.child.another_child_int);
+}
+
+#[test]
+fn named2named_children() {
+ let dto = PersonDto {
+ name: String::from("John"),
+ pets: vec![
+ PetDto {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ PetDto {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let p: Person = dto.try_into().unwrap();
+
+ assert_eq!("John", p.name);
+ assert_eq!(2, p.pets.len());
+ assert_eq!(5, p.pets[0].age);
+ assert_eq!("Mr. Dog", p.pets[0].nickname);
+ assert_eq!(10, p.pets[1].age);
+ assert_eq!("Mr. Cat", p.pets[1].nickname);
+}
+
+#[test]
+fn named2named_children_reverse() {
+ let p = Person {
+ name: String::from("John"),
+ pets: vec![
+ Pet {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ Pet {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let dto: PersonDto = p.try_into().unwrap();
+
+ assert_eq!("John", dto.name);
+ assert_eq!(2, dto.pets.len());
+ assert_eq!(5, dto.pets[0].age);
+ assert_eq!("Mr. Dog", dto.pets[0].nickname);
+ assert_eq!(10, dto.pets[1].age);
+ assert_eq!("Mr. Cat", dto.pets[1].nickname);
+}
+
+#[test]
+fn named2named_children_ref() {
+ let dto = &PersonDto {
+ name: String::from("John"),
+ pets: vec![
+ PetDto {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ PetDto {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let p: Person = dto.try_into().unwrap();
+
+ assert_eq!(dto.name, p.name);
+ assert_eq!(2, p.pets.len());
+ assert_eq!(dto.pets[0].age, p.pets[0].age);
+ assert_eq!(dto.pets[0].nickname, p.pets[0].nickname);
+ assert_eq!(dto.pets[1].age, p.pets[1].age);
+ assert_eq!(dto.pets[1].nickname, p.pets[1].nickname);
+}
+
+#[test]
+fn named2named_children_ref_reversed() {
+ let p = &Person {
+ name: String::from("John"),
+ pets: vec![
+ Pet {
+ age: 5,
+ nickname: String::from("Mr. Dog")
+ },
+ Pet {
+ age: 10,
+ nickname: String::from("Mr. Cat")
+ }
+ ]
+ };
+
+ let dto: PersonDto = p.try_into().unwrap();
+
+ assert_eq!(dto.name, p.name);
+ assert_eq!(2, p.pets.len());
+ assert_eq!(dto.pets[0].age, p.pets[0].age);
+ assert_eq!(dto.pets[0].nickname, p.pets[0].nickname);
+ assert_eq!(dto.pets[1].age, p.pets[1].age);
+ assert_eq!(dto.pets[1].nickname, p.pets[1].nickname);
+}
+
+#[test]
+fn existing_named2named_different_types(){
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 321,
+ some_float: 456.0,
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(321, named.another_int);
+ assert_eq!(456.0, named.some_float);
+
+ let dto = NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.another_int);
+ assert_eq!(456.0, model.some_float);
+}
+
+#[test]
+fn existing_named2named_different_types_ref(){
+ let dto = &NamedStructDto{
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0,
+ };
+
+ let mut named: NamedStruct = Default::default();
+ dto.try_into_existing(&mut named).unwrap();
+
+ assert_eq!(dto.some_int, named.some_int);
+ assert_eq!(dto.another_int, named.another_int as i16);
+ assert_eq!(dto.some_float, named.some_float as f64);
+
+ let mut model: NamedStructModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.some_int, model.some_int);
+ assert_eq!(dto.another_int, model.another_int as i16);
+ assert_eq!(dto.some_float, model.some_float as f64);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/5_field_level_closure_only_attr_tests_fallible.rs b/o2o-tests/tests/5_field_level_closure_only_attr_tests_fallible.rs
new file mode 100644
index 0000000..dab6610
--- /dev/null
+++ b/o2o-tests/tests/5_field_level_closure_only_attr_tests_fallible.rs
@@ -0,0 +1,135 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Parent {
+ child: Child,
+ parent_int: i32,
+}
+
+#[derive(Default)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(Parent, anyhow::Error)]
+#[try_into_existing(Parent, anyhow::Error)]
+struct ParentDto {
+ #[map((&@.child).try_into()?)]
+ child: ChildDto,
+ parent_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(Child, anyhow::Error)]
+struct ChildDto {
+ child_int: i32,
+ #[map(another_child_int)]
+ diff_another_child_int: i32,
+}
+
+#[test]
+fn named2named_child() {
+ let p = Parent {
+ parent_int: 123,
+ child: Child {
+ child_int: 321,
+ another_child_int: 456,
+ },
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.child.child_int);
+ assert_eq!(456, dto.child.diff_another_child_int);
+}
+
+#[test]
+fn named2named_child_reverse() {
+ let dto = ParentDto {
+ parent_int: 123,
+ child: ChildDto {
+ child_int: 321,
+ diff_another_child_int: 456,
+ },
+ };
+
+ let parent: Parent = dto.try_into().unwrap();
+
+ assert_eq!(123, parent.parent_int);
+ assert_eq!(321, parent.child.child_int);
+ assert_eq!(456, parent.child.another_child_int);
+}
+
+#[test]
+fn named2named_child_ref() {
+ let p = &Parent {
+ parent_int: 123,
+ child: Child {
+ child_int: 321,
+ another_child_int: 456,
+ },
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(p.parent_int, dto.parent_int);
+ assert_eq!(p.child.child_int, dto.child.child_int);
+ assert_eq!(p.child.another_child_int, dto.child.diff_another_child_int);
+}
+
+#[test]
+fn named2named_child_ref_reverse() {
+ let dto = &ParentDto {
+ parent_int: 123,
+ child: ChildDto {
+ child_int: 321,
+ diff_another_child_int: 456,
+ },
+ };
+
+ let parent: Parent = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, parent.parent_int);
+ assert_eq!(dto.child.child_int, parent.child.child_int);
+ assert_eq!(dto.child.diff_another_child_int, parent.child.another_child_int);
+}
+
+#[test]
+fn existing_named2named_child_reverse() {
+ let dto = ParentDto {
+ parent_int: 123,
+ child: ChildDto {
+ child_int: 321,
+ diff_another_child_int: 456,
+ },
+ };
+
+ let mut parent: Parent = Default::default();
+ dto.try_into_existing(&mut parent).unwrap();
+
+ assert_eq!(123, parent.parent_int);
+ assert_eq!(321, parent.child.child_int);
+ assert_eq!(456, parent.child.another_child_int);
+}
+
+#[test]
+fn existing_named2named_child_ref_reverse() {
+ let dto = &ParentDto {
+ parent_int: 123,
+ child: ChildDto {
+ child_int: 321,
+ diff_another_child_int: 456,
+ },
+ };
+
+ let mut parent: Parent = Default::default();
+ dto.try_into_existing(&mut parent).unwrap();
+
+ assert_eq!(dto.parent_int, parent.parent_int);
+ assert_eq!(dto.child.child_int, parent.child.child_int);
+ assert_eq!(dto.child.diff_another_child_int, parent.child.another_child_int);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/6_field_level_ident_and_inline_expr_tests_fallible.rs b/o2o-tests/tests/6_field_level_ident_and_inline_expr_tests_fallible.rs
new file mode 100644
index 0000000..55c45df
--- /dev/null
+++ b/o2o-tests/tests/6_field_level_ident_and_inline_expr_tests_fallible.rs
@@ -0,0 +1,144 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Parent {
+ child: Child,
+ parent_int: i32,
+}
+
+#[derive(Default)]
+struct ParentModel {
+ child_diff: Child,
+ parent_int: i32,
+}
+
+#[derive(Default)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[o2o(
+ try_map_owned(Parent, anyhow::Error),
+ try_map_owned(ParentModel, anyhow::Error),
+ owned_try_into_existing(Parent, anyhow::Error),
+ owned_try_into_existing(ParentModel, anyhow::Error),
+)]
+struct ParentDto {
+ #[o2o(
+ from_owned(Parent| @.child.try_into()?),
+ owned_into(Parent| child, ~.try_into()?),
+ from_owned(ParentModel| @.child_diff.try_into()?),
+ owned_into(ParentModel| child_diff, @.diff_child.try_into()?),
+ )]
+ diff_child: ChildDto,
+ parent_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(Child, anyhow::Error)]
+struct ChildDto {
+ #[from(~ as i16)]
+ #[into(~ as i32)]
+ child_int: i16,
+ #[from(@.another_child_int as i8)]
+ #[into(another_child_int, ~ as i32)]
+ diff_another_child_int: i8,
+}
+
+#[test]
+fn named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let p: Parent = dto.try_into().unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let model: ParentModel = dto.try_into().unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
+
+#[test]
+fn named2named_different_name_and_type_reverse() {
+ let p = Parent{
+ parent_int: 987,
+ child: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+
+ let model = ParentModel{
+ parent_int: 987,
+ child_diff: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = model.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+}
+
+#[test]
+fn existing_named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut p: Parent = Default::default();
+ dto.try_into_existing(&mut p).unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut model: ParentModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/7_field_level_ident_and_inline_expr_more_tests_fallible.rs b/o2o-tests/tests/7_field_level_ident_and_inline_expr_more_tests_fallible.rs
new file mode 100644
index 0000000..fb88cd5
--- /dev/null
+++ b/o2o-tests/tests/7_field_level_ident_and_inline_expr_more_tests_fallible.rs
@@ -0,0 +1,219 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Parent {
+ child: Child,
+ parent_int: i32,
+}
+
+#[derive(Default)]
+struct ParentModel {
+ child_diff: Child,
+ parent_int: i32,
+}
+
+#[derive(Default)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(Parent, anyhow::Error)]
+#[try_map(ParentModel, anyhow::Error)]
+#[try_into_existing(Parent, anyhow::Error)]
+#[try_into_existing(ParentModel, anyhow::Error)]
+struct ParentDto {
+ #[from(Parent| (&@.child).try_into()?)]
+ #[into(Parent| child, (&@.diff_child).try_into()?)]
+ #[from(ParentModel| (&@.child_diff).try_into()?)]
+ #[into(ParentModel| child_diff, (&@.diff_child).try_into()?)]
+ diff_child: ChildDto,
+ parent_int: i32,
+}
+
+#[derive(o2o)]
+#[o2o(try_map(Child, anyhow::Error))]
+struct ChildDto {
+ #[o2o(from(@.child_int as i16))]
+ #[o2o(into(@.child_int as i32))]
+ child_int: i16,
+ #[o2o(from(@.another_child_int as i8))]
+ #[o2o(into(another_child_int, @.diff_another_child_int as i32))]
+ diff_another_child_int: i8,
+}
+
+#[test]
+fn named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let p: Parent = dto.try_into().unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let model: ParentModel = dto.try_into().unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
+
+#[test]
+fn named2named_different_name_and_type_reverse() {
+ let p = Parent{
+ parent_int: 987,
+ child: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+
+ let model = ParentModel{
+ parent_int: 987,
+ child_diff: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = model.try_into().unwrap();
+
+ assert_eq!(987, dto.parent_int);
+ assert_eq!(456, dto.diff_child.child_int);
+ assert_eq!(123, dto.diff_child.diff_another_child_int);
+}
+
+#[test]
+fn named2named_different_name_and_type_ref() {
+ let dto = &ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let p: Parent = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, p.parent_int);
+ assert_eq!(dto.diff_child.child_int, p.child.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, p.child.another_child_int as i8);
+
+ let model: ParentModel = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, model.parent_int);
+ assert_eq!(dto.diff_child.child_int, model.child_diff.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, model.child_diff.another_child_int as i8);
+}
+
+#[test]
+fn named2named_different_name_and_type_reverse_ref() {
+ let p = &Parent{
+ parent_int: 987,
+ child: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = p.try_into().unwrap();
+
+ assert_eq!(p.parent_int, dto.parent_int);
+ assert_eq!(p.child.child_int, dto.diff_child.child_int as i32);
+ assert_eq!(p.child.another_child_int, dto.diff_child.diff_another_child_int as i32);
+
+ let model = &ParentModel{
+ parent_int: 987,
+ child_diff: Child {
+ child_int: 456,
+ another_child_int: 123
+ }
+ };
+
+ let dto: ParentDto = model.try_into().unwrap();
+
+ assert_eq!(model.parent_int, dto.parent_int);
+ assert_eq!(model.child_diff.child_int, dto.diff_child.child_int as i32);
+ assert_eq!(model.child_diff.another_child_int, dto.diff_child.diff_another_child_int as i32);
+}
+
+#[test]
+fn existing_named2named_different_name_and_type() {
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut p: Parent = Default::default();
+ dto.try_into_existing(&mut p).unwrap();
+
+ assert_eq!(987, p.parent_int);
+ assert_eq!(456, p.child.child_int);
+ assert_eq!(123, p.child.another_child_int);
+
+ let dto = ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut model: ParentModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(987, model.parent_int);
+ assert_eq!(456, model.child_diff.child_int);
+ assert_eq!(123, model.child_diff.another_child_int);
+}
+
+#[test]
+fn existing_named2named_different_name_and_type_ref() {
+ let dto = &ParentDto{
+ parent_int: 987,
+ diff_child: ChildDto {
+ child_int: 456,
+ diff_another_child_int: 123
+ }
+ };
+
+ let mut p: Parent = Default::default();
+ dto.try_into_existing(&mut p).unwrap();
+
+ assert_eq!(dto.parent_int, p.parent_int);
+ assert_eq!(dto.diff_child.child_int, p.child.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, p.child.another_child_int as i8);
+
+ let mut model: ParentModel = Default::default();
+ dto.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.parent_int, model.parent_int);
+ assert_eq!(dto.diff_child.child_int, model.child_diff.child_int as i16);
+ assert_eq!(dto.diff_child.diff_another_child_int, model.child_diff.another_child_int as i8);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/8_top_level_struct_kind_hint_fallible.rs b/o2o-tests/tests/8_top_level_struct_kind_hint_fallible.rs
new file mode 100644
index 0000000..ca98ed2
--- /dev/null
+++ b/o2o-tests/tests/8_top_level_struct_kind_hint_fallible.rs
@@ -0,0 +1,342 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct UnnamedStructDto(i32, i32, f32);
+
+#[derive(Default)]
+struct UnnamedStructModel(i16, i8, f32);
+
+#[derive(o2o)]
+#[o2o(
+ try_map(UnnamedStructDto as (), String),
+ try_map(UnnamedStructModel as (), String),
+ try_into_existing(UnnamedStructDto as (), String),
+ try_into_existing(UnnamedStructModel as (), String),
+)]
+struct NamedStruct {
+ #[try_from(UnnamedStructModel| @.0 as i32)]
+ #[try_into(UnnamedStructModel| ~ as i16)]
+ some_int: i32,
+ #[from(UnnamedStructModel| @.1 as i32)]
+ #[into(UnnamedStructModel| ~ as i8)]
+ another_int: i32,
+ some_float: f32,
+}
+
+#[derive(Default)]
+struct NamedStructDto {
+ some_int: i32,
+ another_int: i32,
+ some_float: f32,
+}
+
+#[derive(Default)]
+struct NamedStructModel {
+ some_int: i16,
+ another_int: i8,
+ some_float: f32,
+}
+
+#[derive(o2o)]
+#[try_map(NamedStructDto as {}, String)]
+#[try_map(NamedStructModel as {}, String)]
+#[try_into_existing(NamedStructDto as {}, String)]
+#[try_into_existing(NamedStructModel as {}, String)]
+struct UnnamedStruct(
+ #[o2o(
+ map(NamedStructDto| some_int),
+ from(NamedStructModel| @.some_int as i32),
+ into(NamedStructModel| some_int, ~ as i16),
+ )]
+ i32,
+ #[o2o(map(NamedStructDto| another_int))]
+ #[o2o(from(NamedStructModel| @.another_int as i32))]
+ #[o2o(into(NamedStructModel| another_int, ~ as i8))]
+ i32,
+ #[o2o(map(some_float))]
+ f32
+);
+
+#[test]
+fn named2unnamed() {
+ let named = NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let dto: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(127, dto.1);
+ assert_eq!(456.0, dto.2);
+
+ let named = NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let model: UnnamedStructModel = named.try_into().unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(127, model.1);
+ assert_eq!(456.0, model.2);
+}
+
+#[test]
+fn named2unnamed_2() {
+ let dto = NamedStructDto {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let unnamed: UnnamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, unnamed.0);
+ assert_eq!(127, unnamed.1);
+ assert_eq!(456.0, unnamed.2);
+
+ let named = NamedStructModel {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let unnamed: UnnamedStruct = named.try_into().unwrap();
+
+ assert_eq!(123, unnamed.0);
+ assert_eq!(127, unnamed.1);
+ assert_eq!(456.0, unnamed.2);
+}
+
+#[test]
+fn unnamed2named() {
+ let dto = UnnamedStructDto(123, 127, 456.0);
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(127, named.another_int);
+ assert_eq!(456.0, named.some_float);
+
+ let unnamed = UnnamedStructModel(123, 127, 456.0);
+
+ let named: NamedStruct = unnamed.try_into().unwrap();
+
+ assert_eq!(123, named.some_int);
+ assert_eq!(127, named.another_int);
+ assert_eq!(456.0, named.some_float);
+}
+
+#[test]
+fn unnamed2named_2() {
+ let unnamed = UnnamedStruct(123, 127, 456.0);
+
+ let dto: NamedStructDto = unnamed.try_into().unwrap();
+
+ assert_eq!(123, dto.some_int);
+ assert_eq!(127, dto.another_int);
+ assert_eq!(456.0, dto.some_float);
+
+ let unnamed = UnnamedStruct(123, 127, 456.0);
+
+ let model: NamedStructModel = unnamed.try_into().unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.another_int);
+ assert_eq!(456.0, model.some_float);
+}
+
+#[test]
+fn named2unnamed_ref() {
+ let named = &NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let dto: UnnamedStructDto = named.try_into().unwrap();
+
+ assert_eq!(named.some_int, dto.0);
+ assert_eq!(named.another_int, dto.1);
+ assert_eq!(named.some_float, dto.2);
+
+ let named = &NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let model: UnnamedStructModel = named.try_into().unwrap();
+
+ assert_eq!(named.some_int as i16, model.0);
+ assert_eq!(named.another_int as i8, model.1);
+ assert_eq!(named.some_float, model.2);
+}
+
+#[test]
+fn named2unnamed_2_ref() {
+ let dto = &NamedStructDto {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let unnamed: UnnamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(dto.some_int, unnamed.0);
+ assert_eq!(dto.another_int, unnamed.1);
+ assert_eq!(dto.some_float, unnamed.2);
+
+ let model = &NamedStructModel {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let unnamed: UnnamedStruct = model.try_into().unwrap();
+
+ assert_eq!(model.some_int as i32, unnamed.0);
+ assert_eq!(model.another_int as i32, unnamed.1);
+ assert_eq!(model.some_float, unnamed.2);
+}
+
+#[test]
+fn unnamed2named_ref() {
+ let dto = &UnnamedStructDto(123, 127, 456.0);
+
+ let named: NamedStruct = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, named.some_int);
+ assert_eq!(dto.1, named.another_int);
+ assert_eq!(dto.2, named.some_float);
+
+ let model = &UnnamedStructModel(123, 127, 456.0);
+
+ let named: NamedStruct = model.try_into().unwrap();
+
+ assert_eq!(model.0 as i32, named.some_int);
+ assert_eq!(model.1 as i32, named.another_int);
+ assert_eq!(model.2, named.some_float);
+}
+
+#[test]
+fn unnamed2named_2_ref() {
+ let unnamed = &UnnamedStruct(123, 127, 456.0);
+
+ let dto: NamedStructDto = unnamed.try_into().unwrap();
+
+ assert_eq!(dto.some_int, dto.some_int);
+ assert_eq!(dto.another_int, dto.another_int);
+ assert_eq!(dto.some_float, dto.some_float);
+
+ let unnamed = &UnnamedStruct(123, 127, 456.0);
+
+ let model: NamedStructModel = unnamed.try_into().unwrap();
+
+ assert_eq!(dto.some_int as i16, model.some_int);
+ assert_eq!(dto.another_int as i8, model.another_int);
+ assert_eq!(dto.some_float, model.some_float);
+}
+
+#[test]
+fn existing_named2unnamed() {
+ let named = NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let mut dto: UnnamedStructDto = Default::default();
+ named.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(127, dto.1);
+ assert_eq!(456.0, dto.2);
+
+ let named = NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let mut model: UnnamedStructModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.0);
+ assert_eq!(127, model.1);
+ assert_eq!(456.0, model.2);
+}
+
+#[test]
+fn existing_unnamed2named() {
+ let unnamed = UnnamedStruct(123, 127, 456.0);
+
+ let mut dto: NamedStructDto = Default::default();
+ unnamed.try_into_existing(&mut dto).unwrap();
+ assert_eq!(123, dto.some_int);
+ assert_eq!(127, dto.another_int);
+ assert_eq!(456.0, dto.some_float);
+
+ let unnamed = UnnamedStruct(123, 127, 456.0);
+
+ let mut model: NamedStructModel = Default::default();
+ unnamed.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(123, model.some_int);
+ assert_eq!(127, model.another_int);
+ assert_eq!(456.0, model.some_float);
+}
+
+#[test]
+fn existing_named2unnamed_ref() {
+ let named = &NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let mut dto: UnnamedStructDto = Default::default();
+ named.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(named.some_int, dto.0);
+ assert_eq!(named.another_int, dto.1);
+ assert_eq!(named.some_float, dto.2);
+
+ let named = &NamedStruct {
+ some_int: 123,
+ another_int: 127,
+ some_float: 456.0
+ };
+
+ let mut model: UnnamedStructModel = Default::default();
+ named.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(named.some_int as i16, model.0);
+ assert_eq!(named.another_int as i8, model.1);
+ assert_eq!(named.some_float, model.2);
+}
+
+#[test]
+fn existing_unnamed2named_2_ref() {
+ let unnamed = &UnnamedStruct(123, 127, 456.0);
+
+ let mut dto: NamedStructDto = Default::default();
+ unnamed.try_into_existing(&mut dto).unwrap();
+
+ assert_eq!(dto.some_int, dto.some_int);
+ assert_eq!(dto.another_int, dto.another_int);
+ assert_eq!(dto.some_float, dto.some_float);
+
+ let unnamed = &UnnamedStruct(123, 127, 456.0);
+
+ let mut model: NamedStructModel = Default::default();
+ unnamed.try_into_existing(&mut model).unwrap();
+
+ assert_eq!(dto.some_int as i16, model.some_int);
+ assert_eq!(dto.another_int as i8, model.another_int);
+ assert_eq!(dto.some_float, model.some_float);
+}
\ No newline at end of file
diff --git a/o2o-tests/tests/9_child_attr_tests_fallible.rs b/o2o-tests/tests/9_child_attr_tests_fallible.rs
new file mode 100644
index 0000000..d01827b
--- /dev/null
+++ b/o2o-tests/tests/9_child_attr_tests_fallible.rs
@@ -0,0 +1,333 @@
+use o2o::o2o;
+use o2o::traits::TryIntoExisting;
+
+#[derive(Default)]
+struct Entity {
+ parent_int: i32,
+ base: BaseEntity,
+ child: Child,
+}
+
+#[derive(Default)]
+struct TupleEntity(i32, TupleBaseEntity, TupleChild);
+
+#[derive(Default)]
+struct BaseEntity {
+ base: Base,
+ base_entity_int: i32,
+}
+
+#[derive(Default)]
+struct TupleBaseEntity(TupleBase, i32);
+
+#[derive(Default)]
+struct Base {
+ base_int_2: i32,
+ another_base_int: i32,
+ some_string: String
+}
+
+#[derive(Default)]
+struct TupleBase(i32, i16);
+
+#[derive(Default)]
+struct Child {
+ child_int: i32,
+ another_child_int: i32,
+}
+
+#[derive(Default)]
+struct TupleChild(i32, i16);
+
+#[derive(o2o)]
+#[o2o(
+ try_map(Entity, String),
+ try_into_existing(Entity, String),
+ children(Entity| base: BaseEntity, base.base: Base, child: Child),
+)]
+struct EntityDto {
+ parent_int: i32,
+ #[child(base.base)] #[map(base_int_2)] base_int: i32,
+ #[child(base.base)] another_base_int: i32,
+ #[child(base.base)] #[map_ref(~.clone())] some_string: String,
+ #[child(base)] base_entity_int: i32,
+ #[child(child)] child_int: i32,
+ #[child(child)] another_child_int: i32,
+}
+
+#[derive(o2o)]
+#[try_map(TupleEntity, String)]
+#[try_into_existing(TupleEntity, String)]
+#[children(1: TupleBaseEntity, 1 .0: TupleBase, 2: TupleChild)]
+struct TupleEntityDto (
+ i32,
+ #[o2o(child(1 .0), map(0))] i32,
+ #[o2o(child(1 .0), map(1))] i16,
+ #[o2o(child(1), map(1))] i32,
+ #[o2o(child(2), map(0))] i32,
+ #[o2o(child(2), map(1))] i16,
+);
+
+#[test]
+fn named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ some_string: "Test".try_into().unwrap(),
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!("Test", entity.base.base.some_string);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse() {
+ let entity = Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456,
+ some_string: "Test".try_into().unwrap()
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.parent_int);
+ assert_eq!(321, dto.base_int);
+ assert_eq!(456, dto.another_base_int);
+ assert_eq!("Test", dto.some_string);
+ assert_eq!(654, dto.base_entity_int);
+ assert_eq!(789, dto.child_int);
+ assert_eq!(987, dto.another_child_int);
+}
+
+#[test]
+fn named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ some_string: "Test".try_into().unwrap(),
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let entity: Entity = dto.try_into().unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.base.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.base.another_base_int);
+ assert_eq!("Test", entity.base.base.some_string);
+ assert_eq!(dto.base_entity_int, entity.base.base_entity_int);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn named2named_reverse_ref() {
+ let entity = &Entity {
+ parent_int: 123,
+ base: BaseEntity {
+ base: Base {
+ base_int_2: 321,
+ another_base_int: 456,
+ some_string: "Test".try_into().unwrap(),
+ },
+ base_entity_int: 654
+ },
+ child: Child {
+ child_int: 789,
+ another_child_int: 987
+ }
+ };
+
+ let dto: EntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.parent_int, dto.parent_int);
+ assert_eq!(entity.base.base.base_int_2, dto.base_int);
+ assert_eq!(entity.base.base.another_base_int, dto.another_base_int);
+ assert_eq!("Test", dto.some_string);
+ assert_eq!(entity.base.base_entity_int, dto.base_entity_int);
+ assert_eq!(entity.child.child_int, dto.child_int);
+ assert_eq!(entity.child.another_child_int, dto.another_child_int);
+}
+
+#[test]
+fn unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse() {
+ let entity = TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(123, dto.0);
+ assert_eq!(321, dto.1);
+ assert_eq!(456, dto.2);
+ assert_eq!(654, dto.3);
+ assert_eq!(789, dto.4);
+ assert_eq!(987, dto.5);
+}
+
+#[test]
+fn unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let entity: TupleEntity = dto.try_into().unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.0.0);
+ assert_eq!(dto.2, entity.1.0.1);
+ assert_eq!(dto.3, entity.1.1);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
+
+#[test]
+fn unnamed2unnamed_reverse_ref() {
+ let entity = &TupleEntity(
+ 123,
+ TupleBaseEntity (
+ TupleBase (
+ 321,
+ 456
+ ),
+ 654
+ ),
+ TupleChild (
+ 789,
+ 987
+ )
+ );
+
+ let dto: TupleEntityDto = entity.try_into().unwrap();
+
+ assert_eq!(entity.0, dto.0);
+ assert_eq!(entity.1.0.0, dto.1);
+ assert_eq!(entity.1.0.1, dto.2);
+ assert_eq!(entity.1.1, dto.3);
+ assert_eq!(entity.2.0, dto.4);
+ assert_eq!(entity.2.1, dto.5);
+}
+
+#[test]
+fn existing_named2named() {
+ let dto = EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ some_string: "Test".try_into().unwrap(),
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.parent_int);
+ assert_eq!(321, entity.base.base.base_int_2);
+ assert_eq!(456, entity.base.base.another_base_int);
+ assert_eq!("Test", entity.base.base.some_string);
+ assert_eq!(654, entity.base.base_entity_int);
+ assert_eq!(789, entity.child.child_int);
+ assert_eq!(987, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_named2named_ref() {
+ let dto = &EntityDto {
+ parent_int: 123,
+ base_int: 321,
+ another_base_int: 456,
+ some_string: "Test".try_into().unwrap(),
+ base_entity_int: 654,
+ child_int: 789,
+ another_child_int: 987
+ };
+
+ let mut entity: Entity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.parent_int, entity.parent_int);
+ assert_eq!(dto.base_int, entity.base.base.base_int_2);
+ assert_eq!(dto.another_base_int, entity.base.base.another_base_int);
+ assert_eq!("Test", entity.base.base.some_string);
+ assert_eq!(dto.base_entity_int, entity.base.base_entity_int);
+ assert_eq!(dto.child_int, entity.child.child_int);
+ assert_eq!(dto.another_child_int, entity.child.another_child_int);
+}
+
+#[test]
+fn existing_unnamed2unnamed() {
+ let dto = TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(123, entity.0);
+ assert_eq!(321, entity.1.0.0);
+ assert_eq!(456, entity.1.0.1);
+ assert_eq!(654, entity.1.1);
+ assert_eq!(789, entity.2.0);
+ assert_eq!(987, entity.2.1);
+}
+
+#[test]
+fn existing_unnamed2unnamed_ref() {
+ let dto = &TupleEntityDto(123, 321, 456, 654, 789, 987);
+
+ let mut entity: TupleEntity = Default::default();
+ dto.try_into_existing(&mut entity).unwrap();
+
+ assert_eq!(dto.0, entity.0);
+ assert_eq!(dto.1, entity.1.0.0);
+ assert_eq!(dto.2, entity.1.0.1);
+ assert_eq!(dto.3, entity.1.1);
+ assert_eq!(dto.4, entity.2.0);
+ assert_eq!(dto.5, entity.2.1);
+}
\ No newline at end of file
diff --git a/src/traits.rs b/src/traits.rs
index c7c1e0d..7cd0474 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -1,3 +1,8 @@
pub trait IntoExisting {
- fn into_existing(self, other: &mut T) ;
+ fn into_existing(self, other: &mut T);
+}
+
+pub trait TryIntoExisting {
+ type Error;
+ fn try_into_existing(self, other: &mut T) -> Result<(), Self::Error>;
}
\ No newline at end of file