From a6412df0f213f7e3c61cebf3fd10aff06434a5ed Mon Sep 17 00:00:00 2001 From: Artem Romanenia Date: Thu, 20 Jun 2024 15:46:25 +0300 Subject: [PATCH] Features/try (#10) Fallible conversions impl --- README.md | 103 +- o2o-impl/src/attr.rs | 128 ++- o2o-impl/src/expand.rs | 298 +++++- o2o-impl/src/tests.rs | 138 ++- o2o-impl/src/validate.rs | 62 +- o2o-macros/src/lib.rs | 47 +- o2o-tests/Cargo.toml | 1 + .../10_multiple_child_attr_tests_fallible.rs | 616 ++++++++++++ ..._attr_mixed_struct_kinds_tests_fallible.rs | 319 +++++++ .../tests/12_ghost_attr_props_fallible.rs | 522 ++++++++++ .../13_multiple_ghost_attr_props_fallible.rs | 897 ++++++++++++++++++ .../tests/14_parent_attr_tests_fallible.rs | 379 ++++++++ .../15_multiple_parent_attr_tests_fallible.rs | 826 ++++++++++++++++ ..._attr_mixed_struct_kinds_tests_fallible.rs | 380 ++++++++ .../tests/17_generic_path_tests_fallible.rs | 227 +++++ .../tests/18_where_attr_tests_fallible.rs | 232 +++++ .../19_deep_ghost_attr_tests_fallible.rs | 412 ++++++++ .../1_bare_top_level_attr_tests_fallible.rs | 282 ++++++ .../tests/20_as_type_attr_tests_fallible.rs | 211 ++++ .../tests/21_repeat_attr_tests_fallible.rs | 287 ++++++ .../tests/22_nameless_tuple_tests_fallible.rs | 87 ++ o2o-tests/tests/23_pre_init_tests_fallible.rs | 107 +++ .../tests/24_quick_return_tests_fallible.rs | 108 +++ .../tests/25_update_struct_tests_fallible.rs | 77 ++ ...tilde_inside_inline_expr_tests_fallible.rs | 394 ++++++++ ...enum_bare_top_level_attr_tests_fallible.rs | 61 ++ ..._member_level_ident_only_tests_fallible.rs | 67 ++ ...r_level_inline_expr_only_tests_fallible.rs | 36 + ...ld_level_ident_only_attr_tests_fallible.rs | 341 +++++++ ...el_ident_and_inline_expr_tests_fallible.rs | 36 + .../tests/31_enum_literal_tests_fallible.rs | 79 ++ .../tests/32_enum_pattern_tests_fallible.rs | 75 ++ .../33_enum_literal_pattern_tests_fallible.rs | 67 ++ ...inline_at_expr_only_attr_tests_fallible.rs | 515 ++++++++++ ...ine_tilde_expr_only_attr_tests_fallible.rs | 382 ++++++++ ..._level_closure_only_attr_tests_fallible.rs | 135 +++ ...el_ident_and_inline_expr_tests_fallible.rs | 144 +++ ...ent_and_inline_expr_more_tests_fallible.rs | 219 +++++ .../8_top_level_struct_kind_hint_fallible.rs | 342 +++++++ .../tests/9_child_attr_tests_fallible.rs | 333 +++++++ src/traits.rs | 7 +- 41 files changed, 9842 insertions(+), 137 deletions(-) create mode 100644 o2o-tests/tests/10_multiple_child_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/11_child_attr_mixed_struct_kinds_tests_fallible.rs create mode 100644 o2o-tests/tests/12_ghost_attr_props_fallible.rs create mode 100644 o2o-tests/tests/13_multiple_ghost_attr_props_fallible.rs create mode 100644 o2o-tests/tests/14_parent_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/15_multiple_parent_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/16_parent_attr_mixed_struct_kinds_tests_fallible.rs create mode 100644 o2o-tests/tests/17_generic_path_tests_fallible.rs create mode 100644 o2o-tests/tests/18_where_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/19_deep_ghost_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/1_bare_top_level_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/20_as_type_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/21_repeat_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/22_nameless_tuple_tests_fallible.rs create mode 100644 o2o-tests/tests/23_pre_init_tests_fallible.rs create mode 100644 o2o-tests/tests/24_quick_return_tests_fallible.rs create mode 100644 o2o-tests/tests/25_update_struct_tests_fallible.rs create mode 100644 o2o-tests/tests/26_at_and_tilde_inside_inline_expr_tests_fallible.rs create mode 100644 o2o-tests/tests/27_enum_bare_top_level_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/28_enum_member_level_ident_only_tests_fallible.rs create mode 100644 o2o-tests/tests/29_enum_member_level_inline_expr_only_tests_fallible.rs create mode 100644 o2o-tests/tests/2_field_level_ident_only_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests_fallible.rs create mode 100644 o2o-tests/tests/31_enum_literal_tests_fallible.rs create mode 100644 o2o-tests/tests/32_enum_pattern_tests_fallible.rs create mode 100644 o2o-tests/tests/33_enum_literal_pattern_tests_fallible.rs create mode 100644 o2o-tests/tests/3_field_level_inline_at_expr_only_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/4_field_level_inline_tilde_expr_only_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/5_field_level_closure_only_attr_tests_fallible.rs create mode 100644 o2o-tests/tests/6_field_level_ident_and_inline_expr_tests_fallible.rs create mode 100644 o2o-tests/tests/7_field_level_ident_and_inline_expr_more_tests_fallible.rs create mode 100644 o2o-tests/tests/8_top_level_struct_kind_hint_fallible.rs create mode 100644 o2o-tests/tests/9_child_attr_tests_fallible.rs 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. ================================ [github.com](https://github.com/Artem-Romanenia/o2o/) [crates.io](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