Skip to content

Commit

Permalink
Don't trip over tricky unknown attributes; fix skip_repeat
Browse files Browse the repository at this point in the history
  • Loading branch information
Artem-Romanenia committed Jun 28, 2024
1 parent b63ac41 commit af91e02
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 20 deletions.
15 changes: 11 additions & 4 deletions o2o-impl/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub(crate) enum MemberInstruction {
Pat(PatAttr),
VariantTypeHint(VariantTypeHintAttr),
Repeat(MemberRepeatAttr),
SkipRepeat,
StopRepeat,

Misplaced { instr: &'static str, span: Span, own: bool },
Expand Down Expand Up @@ -290,6 +291,7 @@ pub(crate) struct MemberAttrs {
pub lit_attrs: Vec<LitAttr>,
pub pat_attrs: Vec<PatAttr>,
pub repeat: Option<MemberRepeatAttr>,
pub skip_repeat: bool,
pub stop_repeat: bool,
pub type_hint_attrs: Vec<VariantTypeHintAttr>,

Expand Down Expand Up @@ -371,6 +373,10 @@ impl<'a> MemberAttrs {
}

pub(crate) fn merge(&'a mut self, other: Self) {
if self.skip_repeat {
return
}

if let Some(repeat) = other.repeat {
if repeat.repeat_for[&MemberAttrType::Attr] {
self.attrs.extend(other.attrs);
Expand Down Expand Up @@ -967,8 +973,7 @@ pub(crate) fn get_data_type_attrs(input: &[Attribute]) -> Result<(DataTypeAttrs,
instrs.extend(new_instrs.into_iter());
Ok(())
})?;
} else {
let instr = x.path.get_ident().unwrap();
} else if let Some(instr) = x.path.get_ident() {
let p: OptionalParenthesizedTokenStream = syn::parse2(x.tokens.clone())?;
instrs.push(parse_data_type_instruction(instr, p.content(), false, bark)?);
}
Expand Down Expand Up @@ -1026,8 +1031,7 @@ pub(crate) fn get_member_attrs(input: SynDataTypeMember, bark: bool) -> Result<M
instrs.extend(new_instrs.into_iter());
Ok(())
})?;
} else {
let instr = x.path.get_ident().unwrap();
} else if let Some(instr) = x.path.get_ident() {
let p: OptionalParenthesizedTokenStream = syn::parse2(x.tokens.clone())?;
instrs.push(parse_member_instruction(instr, p.content(), false, bark)?);
}
Expand All @@ -1051,6 +1055,7 @@ pub(crate) fn get_member_attrs(input: SynDataTypeMember, bark: bool) -> Result<M
MemberInstruction::Lit(attr) => attrs.lit_attrs.push(attr),
MemberInstruction::Pat(attr) => attrs.pat_attrs.push(attr),
MemberInstruction::Repeat(repeat_for) => attrs.repeat = Some(repeat_for),
MemberInstruction::SkipRepeat => attrs.skip_repeat = true,
MemberInstruction::StopRepeat => attrs.stop_repeat = true,
MemberInstruction::VariantTypeHint(attr) => attrs.type_hint_attrs.push(attr),
MemberInstruction::Unrecognized => (),
Expand Down Expand Up @@ -1116,6 +1121,7 @@ fn parse_data_type_instruction(instr: &Ident, input: TokenStream, own_instr: boo
"literal" if bark => Ok(DataTypeInstruction::Misplaced { instr: "literal", span: instr.span(), own: own_instr }),
"pattern" if bark => Ok(DataTypeInstruction::Misplaced { instr: "pattern", span: instr.span(), own: own_instr }),
"repeat" if bark => Ok(DataTypeInstruction::Misplaced { instr: "repeat", span: instr.span(), own: own_instr }),
"skip_repeat" if bark => Ok(DataTypeInstruction::Misplaced { instr: "skip_repeat", span: instr.span(), own: own_instr }),
"stop_repeat" if bark => Ok(DataTypeInstruction::Misplaced { instr: "stop_repeat", span: instr.span(), own: own_instr }),
"type_hint" if bark => Ok(DataTypeInstruction::Misplaced { instr: "type_hint", span: instr.span(), own: own_instr }),
_ if own_instr => Ok(DataTypeInstruction::UnrecognizedWithError { instr: instr_str.clone(), span: instr.span() }),
Expand Down Expand Up @@ -1184,6 +1190,7 @@ fn parse_member_instruction(instr: &Ident, input: TokenStream, own_instr: bool,
"literal" => Ok(MemberInstruction::Lit(syn::parse2(input)?)),
"pattern" => Ok(MemberInstruction::Pat(syn::parse2(input)?)),
"repeat" => Ok(MemberInstruction::Repeat(syn::parse2(input)?)),
"skip_repeat" => Ok(MemberInstruction::SkipRepeat),
"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 }),
Expand Down
80 changes: 66 additions & 14 deletions o2o-impl/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ fn missing_map_instructions(code_fragment: TokenStream) {
#[stop_repeat(EntityDto)]
enum Enum {}
}, vec![ "Member instruction 'stop_repeat' should be used on a member. To turn this message off, use #[o2o(allow_unknown)]" ]; "enum_misplaced_stop_repeat_instr")]
#[test_case(quote! {
#[map(EntityDto)]
#[skip_repeat(EntityDto)]
struct Entity {}
}, vec![ "Member instruction 'skip_repeat' should be used on a member. To turn this message off, use #[o2o(allow_unknown)]" ]; "struct_misplaced_skip_repeat_instr")]
#[test_case(quote! {
#[map(EntityDto)]
#[skip_repeat(EntityDto)]
enum Enum {}
}, vec![ "Member instruction 'skip_repeat' should be used on a member. To turn this message off, use #[o2o(allow_unknown)]" ]; "enum_misplaced_skip_repeat_instr")]
#[test_case(quote! {
#[map(EntityDto)]
#[type_hint()]
Expand Down Expand Up @@ -217,6 +227,16 @@ fn missing_map_instructions(code_fragment: TokenStream) {
#[o2o(stop_repeat(EntityDto))]
enum Enum {}
}, vec![ "Member instruction 'stop_repeat' should be used on a member." ]; "own_enum_misplaced_stop_repeat_instr")]
#[test_case(quote! {
#[map(EntityDto)]
#[o2o(skip_repeat(EntityDto))]
struct Entity {}
}, vec![ "Member instruction 'skip_repeat' should be used on a member." ]; "own_struct_misplaced_skip_repeat_instr")]
#[test_case(quote! {
#[map(EntityDto)]
#[o2o(skip_repeat(EntityDto))]
enum Enum {}
}, vec![ "Member instruction 'skip_repeat' should be used on a member." ]; "own_enum_misplaced_skip_repeat_instr")]
#[test_case(quote! {
#[o2o(map(EntityDto))]
#[o2o(ghost(EntityDto))]
Expand Down Expand Up @@ -336,11 +356,21 @@ fn unrecognized_member_instructions(code_fragment: TokenStream, err: &str) {
#[mapp(EntityDto)]
struct Entity {}
}; "struct_unrecognized_instr")]
#[test_case(quote! {
#[from_owned(NamedStruct)]
#[mapp::mapp(EntityDto)]
struct Entity {}
}; "struct_unrecognized_path_instr")]
#[test_case(quote! {
#[from_owned(EnumDto)]
#[mapp(EnumDto)]
enum Enum {}
}; "enum_unrecognized_instr")]
#[test_case(quote! {
#[from_owned(EnumDto)]
#[mapp::mapp(EnumDto)]
enum Enum {}
}; "enum_unrecognized_path_instr")]
#[test_case(quote! {
#[o2o(allow_unknown)]
#[map(EntityDto)]
Expand Down Expand Up @@ -395,13 +425,27 @@ fn unrecognized_struct_instructions_no_bark(code_fragment: TokenStream) {
field: i32,
}
}; "struct_unknown_instr")]
#[test_case(quote! {
#[from_owned(NamedStruct)]
struct NamedStructDto {
#[unknown::unknown()]
field: i32,
}
}; "struct_unknown_path_instr")]
#[test_case(quote! {
#[from_owned(EnumDto)]
enum Enum {
#[unknown()]
Variant,
}
}; "enum_unknown_instr")]
#[test_case(quote! {
#[from_owned(EnumDto)]
enum Enum {
#[unkwnown::unknown()]
Variant,
}
}; "enum_unknown_path_instr")]
#[test_case(quote!{
#[from_owned(NamedStruct)]
#[o2o(allow_unknown)]
Expand Down Expand Up @@ -765,59 +809,67 @@ fn dedicated_field_instruction_mismatch(code_fragment: TokenStream, errs: Vec<&s
#[test_case(quote!{
#[map(TestDto| vars(test: {123}), vars(test2: {123}))]
struct Test;
}, "Instruction parameter 'vars' was already set.")]
}, "Instruction parameter 'vars' was already set."; "1")]
#[test_case(quote!{
#[map(TestDto| vars(test: {123}), repeat(), vars(test2: {123}))]
struct Test;
}, "Instruction parameter 'vars' was already set.")]
}, "Instruction parameter 'vars' was already set."; "2")]
#[test_case(quote!{
#[map(TestDto| repeat(), repeat())]
struct Test;
}, "Instruction parameter 'repeat' was already set.")]
}, "Instruction parameter 'repeat' was already set."; "3")]
#[test_case(quote!{
#[map(TestDto| repeat(), vars(test: {123}), repeat())]
struct Test;
}, "Instruction parameter 'repeat' was already set.")]
}, "Instruction parameter 'repeat' was already set."; "4")]
#[test_case(quote!{
#[map(TestDto| skip_repeat, skip_repeat)]
struct Test;
}, "Instruction parameter 'skip_repeat' was already set.")]
}, "Instruction parameter 'skip_repeat' was already set."; "5")]
#[test_case(quote!{
#[map(TestDto| skip_repeat, repeat(), skip_repeat)]
struct Test;
}, "Instruction parameter 'skip_repeat' was already set.")]
}, "Instruction parameter 'skip_repeat' was already set."; "6")]
#[test_case(quote!{
#[map(TestDto| stop_repeat, stop_repeat)]
struct Test;
}, "Instruction parameter 'stop_repeat' was already set.")]
}, "Instruction parameter 'stop_repeat' was already set."; "7")]
#[test_case(quote!{
#[map(TestDto| stop_repeat, repeat(), stop_repeat)]
struct Test;
}, "Instruction parameter 'stop_repeat' was already set.")]
}, "Instruction parameter 'stop_repeat' was already set."; "8")]
#[test_case(quote!{
#[map(TestDto| skip_repeat, skip_repeat)]
struct Test;
}, "Instruction parameter 'skip_repeat' was already set."; "9")]
#[test_case(quote!{
#[map(TestDto| skip_repeat, repeat(), skip_repeat)]
struct Test;
}, "Instruction parameter 'skip_repeat' was already set."; "10")]
#[test_case(quote!{
#[map(TestDto| attribute(test), attribute(test))]
struct Test;
}, "Instruction parameter 'attribute' was already set.")]
}, "Instruction parameter 'attribute' was already set."; "11")]
#[test_case(quote!{
#[map(TestDto| attribute(test), skip_repeat, attribute(test))]
struct Test;
}, "Instruction parameter 'attribute' was already set.")]
}, "Instruction parameter 'attribute' was already set."; "12")]
#[test_case(quote!{
#[map(TestDto| impl_attribute(test), impl_attribute(test))]
struct Test;
}, "Instruction parameter 'impl_attribute' was already set.")]
}, "Instruction parameter 'impl_attribute' was already set."; "13")]
#[test_case(quote!{
#[map(TestDto| impl_attribute(test), skip_repeat, impl_attribute(test))]
struct Test;
}, "Instruction parameter 'impl_attribute' was already set.")]
}, "Instruction parameter 'impl_attribute' was already set."; "14")]
#[test_case(quote!{
#[map(TestDto| inner_attribute(test), inner_attribute(test))]
struct Test;
}, "Instruction parameter 'inner_attribute' was already set.")]
}, "Instruction parameter 'inner_attribute' was already set."; "15")]
#[test_case(quote!{
#[map(TestDto| inner_attribute(test), skip_repeat, inner_attribute(test))]
struct Test;
}, "Instruction parameter 'inner_attribute' was already set.")]
}, "Instruction parameter 'inner_attribute' was already set."; "16")]
fn trait_instruction_defined_twice(code_fragment: TokenStream, err: &str) {
let input: DeriveInput = syn::parse2(code_fragment).unwrap();
let output = derive(&input);
Expand Down
3 changes: 1 addition & 2 deletions o2o-tests/tests/21_repeat_attr_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ struct Machine {
#[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,
Expand All @@ -55,6 +53,7 @@ struct CarDto {
#[o2o(repeat(ghost))] #[ghost({123})]
useless_param: i32,
useless_param_2: i32,
#[o2o(skip_repeat)] number_of_doors: i8,
useless_param_3: i32,
}

Expand Down

0 comments on commit af91e02

Please sign in to comment.