Skip to content

Commit

Permalink
v0.4.1 fix unknown attr handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Artem-Romanenia committed Mar 8, 2024
1 parent 4b2d597 commit 5db0730
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 43 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "o2o"
version = "0.4.0"
version = "0.4.1"
edition = "2021"
authors = ["Artem Romanenia <artem.romanenia@gmail.com>"]
categories = ["rust-patterns"]
Expand All @@ -10,8 +10,8 @@ license = "MIT OR Apache-2.0"
repository = "https://github.com/Artem-Romanenia/o2o"

[dependencies]
o2o-impl = { version = "0.4.0", path = "o2o-impl" }
o2o-macros = { version = "0.4.0", path = "o2o-macros" }
o2o-impl = { version = "0.4.1", path = "o2o-impl" }
o2o-macros = { version = "0.4.1", path = "o2o-macros" }

[workspace]
members = ["o2o-impl", "o2o-macros", "o2o-tests"]
2 changes: 1 addition & 1 deletion o2o-impl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "o2o-impl"
version = "0.4.0"
version = "0.4.1"
edition = "2021"
authors = ["Artem Romanenia <artem.romanenia@gmail.com>"]
description = "Implementation of 'o2o' crate"
Expand Down
20 changes: 14 additions & 6 deletions o2o-impl/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,15 +669,15 @@ pub(crate) fn get_field_attrs(input: &syn::Field, bark: bool) -> Result<FieldAtt
let new_instrs: Punctuated<MemberInstruction, Token![,]> = Punctuated::parse_terminated_with(input, |input| {
let instr = input.parse::<Ident>()?;
let p: OptionalParenthesizedTokenStream = input.parse()?;
parse_member_instruction(&instr, p.content(), true)
parse_member_instruction(&instr, p.content(), true, true)
})?;
instrs.extend(new_instrs.into_iter());
Ok(())
})?;
} else {
let instr = x.path.get_ident().unwrap();
let p: OptionalParenthesizedTokenStream = syn::parse2(x.tokens.clone())?;
instrs.push(parse_member_instruction(instr, p.content(), bark)?);
instrs.push(parse_member_instruction(instr, p.content(), false, bark)?);
}
}
let mut child_attrs: Vec<FieldChildAttr> = vec![];
Expand Down Expand Up @@ -735,14 +735,17 @@ fn parse_struct_instruction(instr: &Ident, input: TokenStream, own_instr: bool,
"ghost" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'ghosts'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"ghost_ref" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'ghosts_ref'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"ghost_owned" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'ghosts_owned'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"parent" if bark => Err(Error::new(instr.span(), format_args!("Struct instruction 'parent' is not supported.{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"child" if bark => Err(Error::new(instr.span(), format_args!("Struct instruction 'child' is not supported.{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"child" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'children'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"parent" if bark => Err(Error::new(instr.span(), format_args!("Member instruction 'parent' should be used on a member.{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"as_type" if bark => Err(Error::new(instr.span(), format_args!("Member instruction 'as_type' should be used on a member.{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"repeat" if bark => Err(Error::new(instr.span(), format_args!("Member instruction 'repeat' should be used on a member.{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"stop_repeat" if bark => Err(Error::new(instr.span(), format_args!("Member instruction 'stop_repeat' should be used on a member.{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
_ if own_instr => Err(Error::new(instr.span(), format_args!("Struct instruction '{}' is not supported.", instr))),
_ => Ok(StructInstruction::Unrecognized),
}
}

fn parse_member_instruction(instr: &Ident, input: TokenStream, bark: bool) -> Result<MemberInstruction> {
fn parse_member_instruction(instr: &Ident, input: TokenStream, own_instr: bool, bark: bool) -> Result<MemberInstruction> {
let instr_str = &instr.to_string();
match instr_str.as_ref() {
"owned_into" | "ref_into" | "into" | "from_owned" | "from_ref" | "from" |
Expand Down Expand Up @@ -778,7 +781,12 @@ fn parse_member_instruction(instr: &Ident, input: TokenStream, bark: bool) -> Re
Ok(MemberInstruction::Repeat(repeat.0))
},
"stop_repeat" => Ok(MemberInstruction::StopRepeat),
_ if bark => Err(Error::new(instr.span(), format_args!("Member instruction '{}' is not supported.", instr))),
"ghosts" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'ghost'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"ghosts_ref" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'ghost_ref'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"ghosts_owned" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'ghost_owned'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"children" if bark => Err(Error::new(instr.span(), format_args!("Perhaps you meant 'child'?{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
"where_clause" if bark => Err(Error::new(instr.span(), format_args!("Struct instruction 'where_clause' should be used on a struct.{}", if !own_instr { " To turn this message off, use #[o2o(allow_unknown)]" } else { "" }))),
_ if own_instr => Err(Error::new(instr.span(), format_args!("Member instruction '{}' is not supported.", instr))),
_ => Ok(MemberInstruction::Unrecognized)
}
}
Expand Down
165 changes: 136 additions & 29 deletions o2o-impl/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,27 @@ fn unrecognized_struct_instructions() {
#[map(EntityDto)]
#[parent(EntityDto)]
struct Entity {}
}, "Struct instruction 'parent' is not supported. To turn this message off, use #[o2o(allow_unknown)]"),
}, "Member instruction 'parent' should be used on a member. To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
#[child(EntityDto)]
struct Entity {}
}, "Struct instruction 'child' is not supported. To turn this message off, use #[o2o(allow_unknown)]"),
}, "Perhaps you meant 'children'? To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(i32)]
#[as_type(i32)]
struct Entity {}
}, "Member instruction 'as_type' should be used on a member. To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
#[repeat(EntityDto)]
struct Entity {}
}, "Member instruction 'repeat' should be used on a member. To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
#[stop_repeat(EntityDto)]
struct Entity {}
}, "Member instruction 'stop_repeat' should be used on a member. To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
#[ghost(EntityDto)]
Expand All @@ -85,12 +100,27 @@ fn unrecognized_struct_instructions() {
#[o2o(map(EntityDto))]
#[o2o(parent(EntityDto))]
struct Entity {}
}, "Struct instruction 'parent' is not supported."),
}, "Member instruction 'parent' should be used on a member."),
(quote! {
#[o2o(map(EntityDto))]
#[o2o(child(EntityDto))]
struct Entity {}
}, "Struct instruction 'child' is not supported."),
}, "Perhaps you meant 'children'?"),
(quote! {
#[map(i32)]
#[o2o(as_type(i32))]
struct Entity {}
}, "Member instruction 'as_type' should be used on a member."),
(quote! {
#[map(EntityDto)]
#[o2o(repeat(EntityDto))]
struct Entity {}
}, "Member instruction 'repeat' should be used on a member."),
(quote! {
#[map(EntityDto)]
#[o2o(stop_repeat(EntityDto))]
struct Entity {}
}, "Member instruction 'stop_repeat' should be used on a member."),
(quote! {
#[o2o(map(EntityDto))]
#[o2o(ghost(EntityDto))]
Expand All @@ -113,53 +143,88 @@ fn unrecognized_member_instructions() {
(quote! {
#[map(EntityDto)]
struct Entity {
#[mapp(diff_field)]
#[ghosts()]
child: i32,
}
}, "mapp"),
}, "Perhaps you meant 'ghost'? To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[children(diff_field)]
#[ghosts_owned()]
child: i32,
}
}, "children"),
}, "Perhaps you meant 'ghost_owned'? To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[where_clause(diff_field)]
#[ghosts_ref()]
child: i32,
}
}, "where_clause"),
}, "Perhaps you meant 'ghost_ref'? To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[children()]
child: i32,
}
}, "Perhaps you meant 'child'? To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[where_clause()]
child: i32,
}
}, "Struct instruction 'where_clause' should be used on a struct. To turn this message off, use #[o2o(allow_unknown)]"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[o2o(mapp(diff_field))]
child: i32,
}
}, "mapp"),
}, "Member instruction 'mapp' is not supported."),
(quote! {
#[map(EntityDto)]
struct Entity {
#[o2o(children(diff_field))]
#[o2o(ghosts())]
child: i32,
}
}, "children"),
}, "Perhaps you meant 'ghost'?"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[o2o(where_clause(diff_field))]
#[o2o(ghosts_owned())]
child: i32,
}
}, "where_clause"),
}, "Perhaps you meant 'ghost_owned'?"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[o2o(ghosts_ref())]
child: i32,
}
}, "Perhaps you meant 'ghost_ref'?"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[o2o(children())]
child: i32,
}
}, "Perhaps you meant 'child'?"),
(quote! {
#[map(EntityDto)]
struct Entity {
#[o2o(where_clause())]
child: i32,
}
}, "Struct instruction 'where_clause' should be used on a struct."),
];

for (code_fragment, err) in test_data {
let input: DeriveInput = syn::parse2(code_fragment).unwrap();
let output = derive(&input);
let message = get_error(output, false);

assert_eq!(message, format!("Member instruction '{}' is not supported.", err));
assert_eq!(message, err);
}
}

Expand Down Expand Up @@ -190,7 +255,6 @@ fn unrecognized_struct_instructions_no_bark() {
struct Entity {}
},
quote!{
#[o2o(allow_unknown)]
#[from_owned(NamedStruct)]
#[unknown()]
struct NamedStructDto {}
Expand All @@ -207,19 +271,62 @@ fn unrecognized_struct_instructions_no_bark() {

#[test]
fn unrecognized_member_instructions_no_bark() {
let code_fragment = quote!{
#[from_owned(NamedStruct)]
#[o2o(allow_unknown)]
struct NamedStructDto {
#[unknown()]
field: i32,
}
};

let input: DeriveInput = syn::parse2(code_fragment).unwrap();
let output = derive(&input);
let test_data = vec![
quote! {
#[from_owned(NamedStruct)]
struct NamedStructDto {
#[unknown()]
field: i32,
}
},
quote!{
#[from_owned(NamedStruct)]
#[o2o(allow_unknown)]
struct NamedStructDto {
#[ghosts()]
field: i32,
}
},
quote!{
#[from_owned(NamedStruct)]
#[o2o(allow_unknown)]
struct NamedStructDto {
#[ghosts_owned()]
field: i32,
}
},
quote!{
#[from_owned(NamedStruct)]
#[o2o(allow_unknown)]
struct NamedStructDto {
#[ghosts_ref()]
field: i32,
}
},
quote!{
#[from_owned(NamedStruct)]
#[o2o(allow_unknown)]
struct NamedStructDto {
#[children()]
field: i32,
}
},
quote!{
#[from_owned(NamedStruct)]
#[o2o(allow_unknown)]
struct NamedStructDto {
#[where_clause()]
field: i32,
}
},
];

assert!(output.is_ok());
for code_fragment in test_data {
let input: DeriveInput = syn::parse2(code_fragment).unwrap();
let output = derive(&input);

assert!(output.is_ok());
}
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions o2o-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "o2o-macros"
version = "0.4.0"
version = "0.4.1"
edition = "2021"
authors = ["Artem Romanenia <artem.romanenia@gmail.com>"]
description = "Macro definitions of 'o2o' crate"
Expand All @@ -11,5 +11,5 @@ repository = "https://github.com/Artem-Romanenia/o2o"
proc-macro = true

[dependencies]
o2o-impl = { version = "0.4.0", path = "../o2o-impl" }
o2o-impl = { version = "0.4.1", path = "../o2o-impl" }
syn = "1.0.3"
4 changes: 2 additions & 2 deletions o2o-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "o2o-tests"
version = "0.4.0"
version = "0.4.1"
edition = "2021"
authors = ["Artem Romanenia <artem.romanenia@gmail.com>"]
description = "Tests for 'o2o' crate"
license = "MIT OR Apache-2.0"
repository = "https://github.com/Artem-Romanenia/o2o"

[dependencies]
o2o = { version = "0.4.0", path = "../" }
o2o = { version = "0.4.1", path = "../" }

0 comments on commit 5db0730

Please sign in to comment.