@@ -142,9 +142,9 @@ fn get_data_struct_schema_def(
142142 let mut record_field_exprs = vec ! [ ] ;
143143 match s. fields {
144144 syn:: Fields :: Named ( ref a) => {
145- let mut index: usize = 0 ;
146145 for field in a. named . iter ( ) {
147146 let mut name = field. ident . as_ref ( ) . unwrap ( ) . to_string ( ) ; // we know everything has a name
147+ let original_name = name. clone ( ) ;
148148 if let Some ( raw_name) = name. strip_prefix ( "r#" ) {
149149 name = raw_name. to_string ( ) ;
150150 }
@@ -163,38 +163,64 @@ fn get_data_struct_schema_def(
163163 }
164164 if let Some ( true ) = field_attrs. skip {
165165 continue ;
166- }
167- let default_value = match field_attrs. default {
168- Some ( default_value) => {
169- let _: serde_json:: Value = serde_json:: from_str ( & default_value[ ..] )
170- . map_err ( |e| {
171- vec ! [ syn:: Error :: new(
172- field. ident. span( ) ,
173- format!( "Invalid avro default json: \n {e}" ) ,
174- ) ]
175- } ) ?;
176- quote ! {
177- Some ( serde_json:: from_str( #default_value) . expect( format!( "Invalid JSON: {:?}" , #default_value) . as_str( ) ) )
178- }
166+ } else if field. attrs . iter ( ) . any ( |attr| {
167+ let mut flatten = false ;
168+ if attr. path ( ) . is_ident ( "serde" ) {
169+ let _ = attr. parse_nested_meta ( |meta| {
170+ if meta. path . is_ident ( "flatten" ) {
171+ flatten = true
172+ }
173+ Ok ( ( ) )
174+ } ) ;
179175 }
180- None => quote ! { None } ,
181- } ;
182- let aliases = preserve_vec ( field_attrs. alias ) ;
183- let schema_expr = type_to_schema_expr ( & field. ty ) ?;
184- let position = index;
185- record_field_exprs. push ( quote ! {
186- apache_avro:: schema:: RecordField {
176+ flatten
177+ } ) {
178+ let ty = & field. ty ;
179+ record_field_exprs. push ( quote ! {
180+ match #ty:: get_schema( ) {
181+ apache_avro:: Schema :: Record ( record) =>
182+ for mut field in record. fields {
183+ field. position = index;
184+ index += 1 ;
185+ schema_fields. push( field) ;
186+ } ,
187+ _ => panic!( "Can not flatten field {}, only fields with a Record schema can be" , #original_name)
188+ }
189+ attributes. insert( apache_avro:: schema:: derive:: SERDE_FLATTEN . to_string( ) , true . into( ) ) ;
190+ } )
191+ } else {
192+ let default_value = match field_attrs. default {
193+ Some ( default_value) => {
194+ let _: serde_json:: Value = serde_json:: from_str ( & default_value[ ..] )
195+ . map_err ( |e| {
196+ vec ! [ syn:: Error :: new(
197+ field. ident. span( ) ,
198+ format!( "Invalid avro default json: \n {e}" ) ,
199+ ) ]
200+ } ) ?;
201+ quote ! {
202+ Some ( serde_json:: from_str( #default_value) . expect( format!( "Invalid JSON: {:?}" , #default_value) . as_str( ) ) )
203+ }
204+ }
205+ None => quote ! { None } ,
206+ } ;
207+ let aliases = preserve_vec ( field_attrs. alias ) ;
208+ let schema_expr = type_to_schema_expr ( & field. ty ) ?;
209+ record_field_exprs. push ( quote ! {
210+ let position = index;
211+ index += 1 ;
212+ schema_fields. push( apache_avro:: schema:: RecordField {
187213 name: #name. to_string( ) ,
188214 doc: #doc,
189215 default : #default_value,
190216 aliases: #aliases,
191217 schema: #schema_expr,
192218 order: apache_avro:: schema:: RecordFieldOrder :: Ascending ,
193- position: #position ,
219+ position,
194220 custom_attributes: Default :: default ( ) ,
195- }
196- } ) ;
197- index += 1 ;
221+ } ) ;
222+ } ) ;
223+ }
198224 }
199225 }
200226 syn:: Fields :: Unnamed ( _) => {
@@ -213,7 +239,10 @@ fn get_data_struct_schema_def(
213239 let record_doc = preserve_optional ( record_doc) ;
214240 let record_aliases = preserve_vec ( aliases) ;
215241 Ok ( quote ! {
216- let schema_fields = vec![ #( #record_field_exprs) , * ] ;
242+ let mut index = 0 ;
243+ let mut schema_fields = vec![ ] ;
244+ let mut attributes: std:: collections:: BTreeMap <String , apache_avro:: schema:: derive:: serde_json:: Value > = Default :: default ( ) ;
245+ #( #record_field_exprs) *
217246 let name = apache_avro:: schema:: Name :: new( #full_schema_name) . expect( & format!( "Unable to parse struct name for schema {}" , #full_schema_name) [ ..] ) ;
218247 let lookup: std:: collections:: BTreeMap <String , usize > = schema_fields
219248 . iter( )
@@ -225,7 +254,7 @@ fn get_data_struct_schema_def(
225254 doc: #record_doc,
226255 fields: schema_fields,
227256 lookup,
228- attributes: Default :: default ( ) ,
257+ attributes,
229258 } )
230259 } )
231260}
@@ -683,7 +712,7 @@ mod tests {
683712 match syn:: parse2 :: < DeriveInput > ( test_struct) {
684713 Ok ( mut input) => {
685714 let schema_res = derive_avro_schema ( & mut input) ;
686- let expected_token_stream = r#"let schema_fields = vec ! [ apache_avro :: schema :: RecordField { name : "a3" . to_string () , doc : Some ("a doc" . into ()) , default : Some (serde_json :: from_str ("123") . expect (format ! ("Invalid JSON: {:?}" , "123") . as_str ())) , aliases : Some (vec ! ["a1" . into () , "a2" . into ()]) , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position : 0usize , custom_attributes : Default :: default () , }] ;"# ;
715+ let expected_token_stream = r#"let position = index ; index += 1 ; schema_fields . push ( apache_avro :: schema :: RecordField { name : "a3" . to_string () , doc : Some ("a doc" . into ()) , default : Some (serde_json :: from_str ("123") . expect (format ! ("Invalid JSON: {:?}" , "123") . as_str ())) , aliases : Some (vec ! ["a1" . into () , "a2" . into ()]) , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position , custom_attributes : Default :: default () , }) ;"# ;
687716 let schema_token_stream = schema_res. unwrap ( ) . to_string ( ) ;
688717 assert ! ( schema_token_stream. contains( expected_token_stream) ) ;
689718 }
@@ -725,7 +754,7 @@ mod tests {
725754 match syn:: parse2 :: < DeriveInput > ( test_struct) {
726755 Ok ( mut input) => {
727756 let schema_res = derive_avro_schema ( & mut input) ;
728- let expected_token_stream = r#"let name = apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse schema name {}" , "A") [..]) . fully_qualified_name (enclosing_namespace) ; let enclosing_namespace = & name . namespace ; if named_schemas . contains_key (& name) { apache_avro :: schema :: Schema :: Ref { name : name . clone () } } else { named_schemas . insert (name . clone () , apache_avro :: schema :: Schema :: Ref { name : name . clone () }) ; let schema_fields = vec ! [apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position : 0usize , custom_attributes : Default :: default () , } , apache_avro :: schema :: RecordField { name : "DOUBLE_ITEM" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position : 1usize , custom_attributes : Default :: default () , }] ;"# ;
757+ let expected_token_stream = r#"let name = apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse schema name {}" , "A") [..]) . fully_qualified_name (enclosing_namespace) ; let enclosing_namespace = & name . namespace ; if named_schemas . contains_key (& name) { apache_avro :: schema :: Schema :: Ref { name : name . clone () } } else { named_schemas . insert (name . clone () , apache_avro :: schema :: Schema :: Ref { name : name . clone () }) ; let mut index = 0 ; let mut schema_fields = vec ! [] ; let mut attributes : std :: collections :: BTreeMap < String , apache_avro :: schema :: derive :: serde_json :: Value > = Default :: default () ; let position = index ; index += 1 ; schema_fields . push (apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position , custom_attributes : Default :: default () , }) ; let position = index ; index += 1 ; schema_fields . push (apache_avro :: schema :: RecordField { name : "DOUBLE_ITEM" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position , custom_attributes : Default :: default () , }) ;"# ;
729758 let schema_token_stream = schema_res. unwrap ( ) . to_string ( ) ;
730759 assert ! ( schema_token_stream. contains( expected_token_stream) ) ;
731760 }
@@ -769,7 +798,7 @@ mod tests {
769798 match syn:: parse2 :: < DeriveInput > ( test_struct) {
770799 Ok ( mut input) => {
771800 let schema_res = derive_avro_schema ( & mut input) ;
772- let expected_token_stream = r#"let name = apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse schema name {}" , "A") [..]) . fully_qualified_name (enclosing_namespace) ; let enclosing_namespace = & name . namespace ; if named_schemas . contains_key (& name) { apache_avro :: schema :: Schema :: Ref { name : name . clone () } } else { named_schemas . insert (name . clone () , apache_avro :: schema :: Schema :: Ref { name : name . clone () }) ; let schema_fields = vec ! [apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position : 0usize , custom_attributes : Default :: default () , } , apache_avro :: schema :: RecordField { name : "DoubleItem" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position : 1usize , custom_attributes : Default :: default () , }] ;"# ;
801+ let expected_token_stream = r#"let name = apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse schema name {}" , "A") [..]) . fully_qualified_name (enclosing_namespace) ; let enclosing_namespace = & name . namespace ; if named_schemas . contains_key (& name) { apache_avro :: schema :: Schema :: Ref { name : name . clone () } } else { named_schemas . insert (name . clone () , apache_avro :: schema :: Schema :: Ref { name : name . clone () }) ; let mut index = 0 ; let mut schema_fields = vec ! [] ; let mut attributes : std :: collections :: BTreeMap < String , apache_avro :: schema :: derive :: serde_json :: Value > = Default :: default () ; let position = index ; index += 1 ; schema_fields . push (apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position , custom_attributes : Default :: default () , }) ; let position = index ; index += 1 ; schema_fields . push (apache_avro :: schema :: RecordField { name : "DoubleItem" . to_string () , doc : None , default : None , aliases : None , schema : apache_avro :: schema :: Schema :: Int , order : apache_avro :: schema :: RecordFieldOrder :: Ascending , position , custom_attributes : Default :: default () , }) ;"# ;
773802 let schema_token_stream = schema_res. unwrap ( ) . to_string ( ) ;
774803 assert ! ( schema_token_stream. contains( expected_token_stream) ) ;
775804 }
0 commit comments