Skip to content

Commit

Permalink
children instr renamed to child_parents
Browse files Browse the repository at this point in the history
  • Loading branch information
Artem-Romanenia committed Nov 27, 2024
1 parent d0de334 commit 35465f7
Show file tree
Hide file tree
Showing 19 changed files with 134 additions and 106 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "o2o"
version = "0.4.11"
edition = "2021"
authors = ["Artem Romanenia <artem.romanenia@gmail.com>"]
categories = ["rust-patterns"]
categories = ["rust-patterns", "no-std", "development-tools"]
description = "Object to Object mapper for Rust. Derive '(Try)From' and '(Try)Into' traits."
keywords = ["from", "into", "copy", "mapper", "derive"]
license = "MIT OR Apache-2.0"
Expand Down
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl From<Person> for PersonDto {
}
```

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

## Basic Example<!-- omit from toc -->

Expand Down Expand Up @@ -94,7 +94,7 @@ And here's the code that `o2o` generates (from here on, generated code is produc
}
impl ::core::convert::TryInto<Person> for PersonDto {
type Error = std::io::Error;
fn try_into(self) -> Result<Person, std::io::Error> {
fn try_into(self) -> ::core::result::Result<Person, std::io::Error> {
Ok(Person {
id: self.id,
name: self.name,
Expand All @@ -118,6 +118,7 @@ And here's the code that `o2o` generates (from here on, generated code is produc

## Some milestones<!-- omit from toc -->

* **v0.5.0** Refactoring and improved support for 'flattened' child fields: `#[child()]`, `#[child_parents()]` and `#[parent()]` instructions
* **v0.4.9** Support for `#![no_std]`
* **v0.4.4** Fallible conversions
* **v0.4.3** Enum-to-primitive type conversions with `#[literal(...)]` and `#[pattern(...)]`
Expand Down Expand Up @@ -501,7 +502,7 @@ struct EntityDto {
}
impl ::core::convert::TryInto<Entity> for EntityDto {
type Error = std::num::ParseIntError;
fn try_into(self) -> Result<Entity, std::num::ParseIntError> {
fn try_into(self) -> ::core::result::Result<Entity, std::num::ParseIntError> {
Ok(Entity {
some_int: self.some_int,
str: self.str,
Expand All @@ -511,7 +512,7 @@ struct EntityDto {
}
impl ::core::convert::TryInto<Entity> for &EntityDto {
type Error = std::num::ParseIntError;
fn try_into(self) -> Result<Entity, std::num::ParseIntError> {
fn try_into(self) -> ::core::result::Result<Entity, std::num::ParseIntError> {
Ok(Entity {
some_int: self.some_int,
str: self.str.clone(),
Expand Down Expand Up @@ -851,7 +852,7 @@ struct Wrapper(#[from(@.parse::<i32>()?)]i32);
``` rust ignore
impl ::core::convert::TryFrom<String> for Wrapper {
type Error = std::num::ParseIntError;
fn try_from(value: String) -> Result<Wrapper, std::num::ParseIntError> {
fn try_from(value: String) -> ::core::result::Result<Wrapper, std::num::ParseIntError> {
Ok(Wrapper(value.parse::<i32>()?))
}
}
Expand Down Expand Up @@ -1133,7 +1134,7 @@ impl EmployeeDto {

#### Child instructions

When the instructions are put on the side that contains flatened properties, conversions `From<T>` and `IntoExisting<T>` only require usage of a member level `#[child(...)]` instruction, which accepts a path to the unflatened field (*without* the field name itself).
When the instructions are put on the side that contains flatened (child) properties, conversions `From<T>` and `IntoExisting<T>` only require usage of a member level `#[child(...)]` instruction, which accepts a dot separated path to the parent field (*without* the field name itself).
``` rust
use o2o::o2o;

Expand Down Expand Up @@ -1192,7 +1193,7 @@ struct CarDto {
```
</details>

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

``` rust
use o2o::o2o;
Expand All @@ -1212,7 +1213,7 @@ struct Machine {

#[derive(o2o)]
#[owned_into(Car)]
#[children(vehicle: Vehicle, vehicle.machine: Machine)]
#[child_parents(vehicle: Vehicle, vehicle.machine: Machine)]
struct CarDto {
number_of_doors: i8,

Expand Down Expand Up @@ -1249,14 +1250,14 @@ struct CarDto {

#### Parent instructions

When the instructions are put on the side that contains parent property that is being flatened, conversions `Into<T>` and `IntoExisting<T>` can be done by using #[parent(...)] instruction and listing inner properties:
When the instructions are put on the side that contains parent property that is being flatened, conversions `Into<T>` and `IntoExisting<T>` can be done by using #[parent(...)] instruction and listing child properties:

``` rust
#[derive(o2o::o2o)]
#[owned_into(CarDto)]
struct Car {
number_of_doors: i8,
#[parent(number_of_seats, [parent(brand, year)] machine)] // [parent] instruction is recursive
#[parent(number_of_seats, [parent(brand, year)] machine)] // [parent] instruction can be recursive
vehicle: Vehicle
}

Expand Down Expand Up @@ -1833,7 +1834,7 @@ struct Machine {

#[derive(o2o)]
#[map_ref(Car)]
#[children(vehicle: Vehicle, vehicle.machine: Machine)]
#[child_parents(vehicle: Vehicle, vehicle.machine: Machine)]
#[ghosts(vehicle.machine@id: {321})]
struct CarDto {
number_of_doors: i8,
Expand Down Expand Up @@ -2083,7 +2084,7 @@ enum EnumWithDataDto {
}
impl ::core::convert::TryInto<EnumWithData> for EnumWithDataDto {
type Error = std::num::ParseIntError;
fn try_into(self) -> Result<EnumWithData, std::num::ParseIntError> {
fn try_into(self) -> ::core::result::Result<EnumWithData, std::num::ParseIntError> {
Ok(match self {
EnumWithDataDto::Item1(f0, f1) => EnumWithData::Item1(f0.parse::<i32>()?, f1),
EnumWithDataDto::Item2 { str, i_str } => EnumWithData::Item2 {
Expand Down Expand Up @@ -2275,7 +2276,7 @@ enum EnumDto {
}
impl ::core::convert::TryInto<Enum> for EnumDto {
type Error = String;
fn try_into(self) -> Result<Enum, String> {
fn try_into(self) -> ::core::result::Result<Enum, String> {
Ok(match self {
EnumDto::Var1 => Enum::Var1,
EnumDto::Var2(f0, f1) => Enum::Var2(f0, f1),
Expand Down Expand Up @@ -2311,7 +2312,7 @@ enum EnumDto {
``` rust ignore
impl ::core::convert::TryFrom<EnumDto> for Enum {
type Error = String;
fn try_from(value: EnumDto) -> Result<Enum, String> {
fn try_from(value: EnumDto) -> ::core::result::Result<Enum, String> {
Ok(match value {
EnumDto::Var1 => Enum::Var1,
EnumDto::Var2(f0, f1) => Enum::Var2(f0, f1),
Expand Down Expand Up @@ -2684,7 +2685,7 @@ enum HttpStatusFamily {
``` rust ignore
impl ::core::convert::TryFrom<i32> for HttpStatus {
type Error = StaticStr;
fn try_from(value: i32) -> Result<HttpStatus, StaticStr> {
fn try_from(value: i32) -> ::core::result::Result<HttpStatus, StaticStr> {
Ok(match value {
200 => HttpStatus::Ok,
201 => HttpStatus::Created,
Expand All @@ -2698,7 +2699,7 @@ enum HttpStatusFamily {
}
impl ::core::convert::TryInto<i32> for HttpStatus {
type Error = StaticStr;
fn try_into(self) -> Result<i32, StaticStr> {
fn try_into(self) -> ::core::result::Result<i32, StaticStr> {
Ok(match self {
HttpStatus::Ok => 200,
HttpStatus::Created => 201,
Expand All @@ -2712,7 +2713,7 @@ enum HttpStatusFamily {

impl ::core::convert::TryFrom<i32> for HttpStatusFamily {
type Error = StaticStr;
fn try_from(value: i32) -> Result<HttpStatusFamily, StaticStr> {
fn try_from(value: i32) -> ::core::result::Result<HttpStatusFamily, StaticStr> {
Ok(match value {
100..=199 => HttpStatusFamily::Information,
200..=299 => HttpStatusFamily::Success,
Expand Down
42 changes: 22 additions & 20 deletions o2o-impl/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub(crate) enum DataTypeInstruction {
Map(TraitAttr),
Ghosts(GhostsAttr),
Where(WhereAttr),
Children(ChildrenAttr),
ChildParents(ChildParentsAttr),
AllowUnknown,

Misplaced { instr: &'static str, span: Span, own: bool },
Expand Down Expand Up @@ -191,7 +191,7 @@ pub(crate) struct DataTypeAttrs {
pub attrs: Vec<TraitAttr>,
pub ghosts_attrs: Vec<GhostsAttr>,
pub where_attrs: Vec<WhereAttr>,
pub children_attrs: Vec<ChildrenAttr>,
pub child_parents_attrs: Vec<ChildParentsAttr>,

pub error_instrs: Vec<DataTypeInstruction>,
}
Expand All @@ -217,10 +217,10 @@ impl<'a> DataTypeAttrs {
.or_else(|| self.where_attrs.iter().find(|x| x.container_ty.is_none()))
}

pub(crate) fn children_attr(&'a self, container_ty: &TypePath) -> Option<&ChildrenAttr>{
self.children_attrs.iter()
pub(crate) fn child_parents_attr(&'a self, container_ty: &TypePath) -> Option<&ChildParentsAttr>{
self.child_parents_attrs.iter()
.find(|x| x.container_ty.is_some() && x.container_ty.as_ref().unwrap() == container_ty)
.or_else(|| self.children_attrs.iter().find(|x| x.container_ty.is_none()))
.or_else(|| self.child_parents_attrs.iter().find(|x| x.container_ty.is_none()))
}
}

Expand Down Expand Up @@ -760,40 +760,40 @@ impl Parse for WhereAttr {
}
}

pub(crate) struct ChildrenAttr {
pub(crate) struct ChildParentsAttr {
pub container_ty: Option<TypePath>,
pub children: Punctuated<ChildData, Token![,]>,
pub child_parents: Punctuated<ChildParentData, Token![,]>,
}

impl Parse for ChildrenAttr {
impl Parse for ChildParentsAttr {
fn parse(input: ParseStream) -> Result<Self> {
Ok(ChildrenAttr {
Ok(ChildParentsAttr {
container_ty: try_parse_container_ident(input, false),
children: try_parse_children(input)?,
child_parents: try_parse_child_parents(input)?,
})
}
}

pub(crate) struct ChildData {
pub(crate) struct ChildParentData {
pub ty: syn::Path,
pub type_hint: TypeHint,
pub field_path: Punctuated<Member, Token![.]>,
field_path_str: String,
}

impl ChildData {
impl ChildParentData {
pub(crate) fn check_match(&self, path: &str) -> bool {
self.field_path_str == path
}
}

impl PartialEq for ChildData {
impl PartialEq for ChildParentData {
fn eq(&self, other: &Self) -> bool {
self.field_path_str == other.field_path_str
}
}
impl Eq for ChildData {}
impl Hash for ChildData {
impl Eq for ChildParentData {}
impl Hash for ChildParentData {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.field_path_str.hash(state)
}
Expand Down Expand Up @@ -1112,7 +1112,7 @@ pub(crate) fn get_data_type_attrs(input: &[Attribute]) -> Result<(DataTypeAttrs,
},
DataTypeInstruction::Ghosts(attr) => attrs.ghosts_attrs.push(attr),
DataTypeInstruction::Where(attr) => attrs.where_attrs.push(attr),
DataTypeInstruction::Children(attr) => attrs.children_attrs.push(attr),
DataTypeInstruction::ChildParents(attr) => attrs.child_parents_attrs.push(attr),
DataTypeInstruction::AllowUnknown | DataTypeInstruction::Unrecognized => (),
_ => attrs.error_instrs.push(instr),
};
Expand Down Expand Up @@ -1208,12 +1208,13 @@ fn parse_data_type_instruction(instr: &Ident, input: TokenStream, own_instr: boo
appl_ghosts_ref(instr_str),
],
})),
"children" => Ok(DataTypeInstruction::Children(syn::parse2(input)?)),
"child_parents" => Ok(DataTypeInstruction::ChildParents(syn::parse2(input)?)),
"where_clause" => Ok(DataTypeInstruction::Where(syn::parse2(input)?)),
"children" => Ok(DataTypeInstruction::Misnamed { instr: "children", span: instr.span(), guess_name: "child_parents", own: own_instr }),
"ghost" if bark => Ok(DataTypeInstruction::Misnamed { instr: "ghost", span: instr.span(), guess_name: "ghosts", own: own_instr }),
"ghost_ref" if bark => Ok(DataTypeInstruction::Misnamed { instr: "ghost_ref", span: instr.span(), guess_name: "ghosts_ref", own: own_instr }),
"ghost_owned" if bark => Ok(DataTypeInstruction::Misnamed { instr: "ghost_owned", span: instr.span(), guess_name: "ghosts_owned", own: own_instr }),
"child" if bark => Ok(DataTypeInstruction::Misnamed { instr: "child", span: instr.span(), guess_name: "children", own: own_instr }),
"child" if bark => Ok(DataTypeInstruction::Misnamed { instr: "child", span: instr.span(), guess_name: "child_parents", own: own_instr }),
"parent" if bark => Ok(DataTypeInstruction::Misplaced { instr: "parent", span: instr.span(), own: own_instr }),
"as_type" if bark => Ok(DataTypeInstruction::Misplaced { instr: "as_type", span: instr.span(), own: own_instr }),
"literal" if bark => Ok(DataTypeInstruction::Misplaced { instr: "literal", span: instr.span(), own: own_instr }),
Expand Down Expand Up @@ -1288,6 +1289,7 @@ fn parse_member_instruction(instr: &Ident, input: TokenStream, own_instr: bool,
"stop_repeat" => Ok(MemberInstruction::StopRepeat),
"type_hint" => Ok(MemberInstruction::VariantTypeHint(syn::parse2(input)?)),
"children" if bark => Ok(MemberInstruction::Misnamed { instr: "children", span: instr.span(), guess_name: "child", own: own_instr }),
"child_parents" if bark => Ok(MemberInstruction::Misnamed { instr: "child_parents", span: instr.span(), guess_name: "child", own: own_instr }),
"where_clause" if bark => Ok(MemberInstruction::Misplaced { instr: "where_clause", span: instr.span(), own: own_instr }),
"allow_unknown" if bark => Ok(MemberInstruction::Misplaced { instr: "allow_unknown", span: instr.span(), own: own_instr }),
_ if own_instr => Ok(MemberInstruction::UnrecognizedWithError { instr: instr_str.clone(), span: instr.span() }),
Expand Down Expand Up @@ -1369,12 +1371,12 @@ fn peek_ghost_field_name(input: ParseStream) -> bool {
peek_member(input) && (input.peek2(Token![:]) || input.peek2(Brace) || input.peek2(Paren))
}

fn try_parse_children(input: ParseStream) -> Result<Punctuated<ChildData, Token![,]>> {
fn try_parse_child_parents(input: ParseStream) -> Result<Punctuated<ChildParentData, Token![,]>> {
input.parse_terminated(|x| {
let child_path: Punctuated<Member, Token![.]> = Punctuated::parse_separated_nonempty(x)?;
x.parse::<Token![:]>()?;
let ty = x.parse::<syn::Path>()?;
Ok(ChildData {
Ok(ChildParentData {
ty,
type_hint: try_parse_type_hint(x)?,
field_path: child_path.clone(),
Expand Down
14 changes: 7 additions & 7 deletions o2o-impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{collections::HashMap, iter::Peekable, slice::Iter};

use crate::{
ast::{DataType, DataTypeMember, Enum, Field, Struct, Variant},
attr::{ApplicableAttr, ChildData, ChildPath, DataTypeAttrs, GhostData, GhostIdent, Kind, MemberAttrCore, ParentChildField, TraitAttrCore, TypeHint},
attr::{ApplicableAttr, ChildParentData, ChildPath, DataTypeAttrs, GhostData, GhostIdent, Kind, MemberAttrCore, ParentChildField, TraitAttrCore, TypeHint},
validate::validate,
};
use proc_macro2::{Span, TokenStream};
Expand Down Expand Up @@ -59,8 +59,8 @@ struct ChildRenderContext<'a> {
pub type_hint: TypeHint
}

impl<'a> From<&'a ChildData> for ChildRenderContext<'a> {
fn from(value: &'a ChildData) -> Self {
impl<'a> From<&'a ChildParentData> for ChildRenderContext<'a> {
fn from(value: &'a ChildParentData) -> Self {
ChildRenderContext { ty: &value.ty, type_hint: value.type_hint }
}
}
Expand Down Expand Up @@ -512,8 +512,8 @@ fn render_child_fragment<F: Fn() -> TokenStream>(
let new_depth = depth.map_or(0, |x|x+1);
match ctx.kind {
Kind::OwnedInto | Kind::RefInto => {
let mut children = ctx.input.get_attrs().children_attr(&ctx.struct_attr.ty).unwrap().children.iter();
let child_data = children.find(|child_data| child_data.check_match(child_path.get_child_path_str(Some(new_depth)))).unwrap();
let mut child_parents = ctx.input.get_attrs().child_parents_attr(&ctx.struct_attr.ty).unwrap().child_parents.iter();
let child_data = child_parents.find(|child_data| child_data.check_match(child_path.get_child_path_str(Some(new_depth)))).unwrap();

render_child(&child_data.into(), fields, ctx.input.named_fields(), ctx, (child_path, new_depth), type_hint)
},
Expand Down Expand Up @@ -633,8 +633,8 @@ fn render_existing_child(
{
let child_attr = field_ctx.0;
let path = child_attr.get_child_path_str(Some(field_ctx.1));
let children_attr = ctx.input.get_attrs().children_attr(&ctx.struct_attr.ty);
let child_data = children_attr.and_then(|x| x.children.iter().find(|child_data| child_data.check_match(path)));
let child_parents_attr = ctx.input.get_attrs().child_parents_attr(&ctx.struct_attr.ty);
let child_data = child_parents_attr.and_then(|x| x.child_parents.iter().find(|child_data| child_data.check_match(path)));
struct_init_block_inner(fields, named_fields, ctx, Some((field_ctx.0, child_data.map(|x|x.into()).as_ref(), field_ctx.1)))
}

Expand Down
Loading

0 comments on commit 35465f7

Please sign in to comment.