@@ -58,7 +58,9 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult<TokenStream> {
5858 let discriminator_name = & args. discriminator_name ;
5959
6060 let Data :: Enum ( e) = & args. data else {
61- return Err ( Error :: new_spanned ( ident, "AnyOf can only be applied to an enum." ) . into ( ) ) ;
61+ return Err (
62+ Error :: new_spanned ( ident, "AnyOf (Union) can only be applied to an enum." ) . into ( ) ,
63+ ) ;
6264 } ;
6365
6466 if discriminator_name. is_some ( ) && args. externally_tagged {
@@ -85,122 +87,45 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult<TokenStream> {
8587 for variant in e {
8688 let item_ident = & variant. ident ;
8789
88- if variant. fields . len ( ) != 1 {
89- return Err ( Error :: new_spanned ( & variant. ident , "Incorrect oneof definition." ) . into ( ) ) ;
90- }
91-
92- let object_ty = & variant. fields . fields [ 0 ] ;
93- let format_string = format ! ( "{{}}_{}" , item_ident) ;
94- let schema_name = quote ! {
95- :: std:: format!( #format_string, <Self as #crate_name:: types:: Type >:: name( ) )
96- } ;
9790 let mapping_name = match & variant. mapping {
9891 Some ( mapping) => mapping. clone ( ) ,
9992 None => apply_rename_rule_variant ( args. rename_all , item_ident. unraw ( ) . to_string ( ) ) ,
10093 } ;
101- types. push ( object_ty) ;
102-
103- if args. externally_tagged {
104- from_json. push ( quote ! {
105- if let value @ :: std:: option:: Option :: Some ( _) = value. as_object( ) . and_then( |obj| obj. get( #mapping_name) ) . cloned( ) {
106- return <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( value)
107- . map( Self :: #item_ident)
108- . map_err( #crate_name:: types:: ParseError :: propagate) ;
109- }
110- } ) ;
111- } else if discriminator_name. is_some ( ) {
112- from_json. push ( quote ! {
113- if :: std:: matches!( discriminator_name, :: std:: option:: Option :: Some ( discriminator_name) if discriminator_name == #mapping_name) {
114- return <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( :: std:: option:: Option :: Some ( value) )
115- . map( Self :: #item_ident)
116- . map_err( #crate_name:: types:: ParseError :: propagate) ;
117- }
118- } ) ;
119- } else if !args. one_of {
120- // any of
121- from_json. push ( quote ! {
122- if let :: std:: result:: Result :: Ok ( obj) = <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( :: std:: option:: Option :: Some ( :: std:: clone:: Clone :: clone( & value) ) )
123- . map( Self :: #item_ident) {
124- return :: std:: result:: Result :: Ok ( obj) ;
125- }
126- } ) ;
127- } else {
128- // one of
129- from_json. push ( quote ! {
130- if let :: std:: result:: Result :: Ok ( obj) = <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( :: std:: option:: Option :: Some ( :: std:: clone:: Clone :: clone( & value) ) )
131- . map( Self :: #item_ident) {
132- if res_obj. is_some( ) {
133- return :: std:: result:: Result :: Err ( #crate_name:: types:: ParseError :: expected_type( value) ) ;
134- }
135- res_obj = Some ( obj) ;
136- }
137- } ) ;
138- }
13994
140- if args. externally_tagged {
141- to_json. push ( quote ! {
142- Self :: #item_ident( obj) => {
143- let value = <#object_ty as #crate_name:: types:: ToJSON >:: to_json( obj) ;
144- let mut wrapped = #crate_name:: __private:: serde_json:: Map :: new( ) ;
145- wrapped. insert( :: std:: convert:: Into :: into( #mapping_name) , :: std:: option:: Option :: unwrap_or_default( value) ) ;
146- :: std:: option:: Option :: Some ( #crate_name:: __private:: serde_json:: Value :: Object ( wrapped) )
147- }
148- } ) ;
149- } else if let Some ( discriminator_name) = & discriminator_name {
150- to_json. push ( quote ! {
151- Self :: #item_ident( obj) => {
152- let mut value = <#object_ty as #crate_name:: types:: ToJSON >:: to_json( obj) ;
153- if let :: std:: option:: Option :: Some ( obj) = value. as_mut( ) . and_then( |value| value. as_object_mut( ) ) {
154- obj. insert( :: std:: convert:: Into :: into( #discriminator_name) , :: std:: convert:: Into :: into( #mapping_name) ) ;
155- }
156- value
157- }
158- } ) ;
159- } else {
160- to_json. push ( quote ! {
161- Self :: #item_ident( obj) => <#object_ty as #crate_name:: types:: ToJSON >:: to_json( obj)
162- } ) ;
163- }
164-
165- mapping. push ( quote ! {
166- ( :: std:: string:: ToString :: to_string( #mapping_name) , :: std:: format!( "#/components/schemas/{}" , #schema_name) )
167- } ) ;
168-
169- if args. externally_tagged {
170- create_schemas. push ( quote ! {
171- let schema = #crate_name:: registry:: MetaSchema {
172- description: #description,
173- all_of: :: std:: vec![
174- #crate_name:: registry:: MetaSchemaRef :: Inline ( :: std:: boxed:: Box :: new( #crate_name:: registry:: MetaSchema {
175- required: :: std:: vec![ #mapping_name] ,
176- properties: :: std:: vec![
177- (
178- #mapping_name,
179- <#object_ty as #crate_name:: types:: Type >:: schema_ref( ) ,
180- )
181- ] ,
182- ..#crate_name:: registry:: MetaSchema :: new( "object" )
183- } ) ) ,
184- ] ,
185- ..#crate_name:: registry:: MetaSchema :: ANY
186- } ;
187- registry. schemas. insert( #schema_name, schema) ;
188- } ) ;
189-
190- schemas. push ( quote ! {
191- #crate_name:: registry:: MetaSchemaRef :: Reference ( #schema_name)
192- } ) ;
193- } else if let Some ( discriminator_name) = & args. discriminator_name {
194- create_schemas. push ( quote ! {
195- {
196- fn __check_is_object_type<T : #crate_name:: types:: IsObjectType >( ) { }
197- __check_is_object_type:: <#object_ty>( ) ;
198- }
199-
200- let schema = #crate_name:: registry:: MetaSchema {
201- description: #description,
202- all_of: :: std:: vec![
203- #crate_name:: registry:: MetaSchemaRef :: Inline ( :: std:: boxed:: Box :: new( #crate_name:: registry:: MetaSchema {
95+ match variant. fields . len ( ) {
96+ 0 => {
97+ if args. externally_tagged {
98+ return Err ( Error :: new_spanned (
99+ & variant. ident ,
100+ "Empty variant cannot be externally tagged." ,
101+ )
102+ . into ( ) ) ;
103+ } else if let Some ( discriminator_name) = & discriminator_name {
104+ from_json. push ( quote ! {
105+ if :: std:: matches!( discriminator_name, :: std:: option:: Option :: Some ( discriminator_name) if discriminator_name == #mapping_name) {
106+ return :: std:: result:: Result :: Ok ( Self :: #item_ident)
107+ }
108+ } ) ;
109+ to_json. push ( quote ! {
110+ Self :: #item_ident => {
111+ :: std:: option:: Option :: Some ( #crate_name:: __private:: serde_json:: json!( { #discriminator_name: #mapping_name } ) )
112+ }
113+ } ) ;
114+
115+ // Create a named schema for the childless variant to support discriminator
116+ // mapping
117+ let format_string = format ! ( "{{}}_{}" , item_ident) ;
118+ let schema_name = quote ! {
119+ :: std:: format!( #format_string, <Self as #crate_name:: types:: Type >:: name( ) )
120+ } ;
121+
122+ mapping. push ( quote ! {
123+ ( :: std:: string:: ToString :: to_string( #mapping_name) , :: std:: format!( "#/components/schemas/{}" , #schema_name) )
124+ } ) ;
125+
126+ create_schemas. push ( quote ! {
127+ let schema = #crate_name:: registry:: MetaSchema {
128+ description: #description,
204129 required: #required,
205130 properties: :: std:: vec![
206131 (
@@ -216,22 +141,179 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult<TokenStream> {
216141 )
217142 ] ,
218143 ..#crate_name:: registry:: MetaSchema :: new( "object" )
219- } ) ) ,
220- <#object_ty as #crate_name:: types:: Type >:: schema_ref( ) ,
221- ] ,
222- ..#crate_name:: registry:: MetaSchema :: ANY
144+ } ;
145+ registry. schemas. insert( #schema_name, schema) ;
146+ } ) ;
147+
148+ schemas. push ( quote ! {
149+ #crate_name:: registry:: MetaSchemaRef :: Reference ( #schema_name)
150+ } ) ;
151+ } else {
152+ from_json. push ( quote ! {
153+ if value. is_object( ) { return :: std:: result:: Result :: Ok ( Self :: #item_ident) ; }
154+ } ) ;
155+ to_json. push ( quote ! {
156+ Self :: #item_ident => :: std:: option:: Option :: Some ( #crate_name:: __private:: serde_json:: json!( { } ) )
157+ } ) ;
158+
159+ // For childless variants without a discriminator, create an inline empty object
160+ // schema
161+ schemas. push ( quote ! {
162+ #crate_name:: registry:: MetaSchemaRef :: Inline ( :: std:: boxed:: Box :: new(
163+ #crate_name:: registry:: MetaSchema :: new( "object" )
164+ ) )
165+ } ) ;
166+ }
167+ }
168+ 1 => {
169+ let object_ty = & variant. fields . fields [ 0 ] ;
170+ let format_string = format ! ( "{{}}_{}" , item_ident) ;
171+ let schema_name = quote ! {
172+ :: std:: format!( #format_string, <Self as #crate_name:: types:: Type >:: name( ) )
223173 } ;
174+ types. push ( object_ty) ;
175+
176+ if args. externally_tagged {
177+ from_json. push ( quote ! {
178+ if let value @ :: std:: option:: Option :: Some ( _) = value. as_object( ) . and_then( |obj| obj. get( #mapping_name) ) . cloned( ) {
179+ return <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( value)
180+ . map( Self :: #item_ident)
181+ . map_err( #crate_name:: types:: ParseError :: propagate) ;
182+ }
183+ } ) ;
184+ } else if discriminator_name. is_some ( ) {
185+ from_json. push ( quote ! {
186+ if :: std:: matches!( discriminator_name, :: std:: option:: Option :: Some ( discriminator_name) if discriminator_name == #mapping_name) {
187+ return <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( :: std:: option:: Option :: Some ( value) )
188+ . map( Self :: #item_ident)
189+ . map_err( #crate_name:: types:: ParseError :: propagate) ;
190+ }
191+ } ) ;
192+ } else if !args. one_of {
193+ // any of
194+ from_json. push ( quote ! {
195+ if let :: std:: result:: Result :: Ok ( obj) = <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( :: std:: option:: Option :: Some ( :: std:: clone:: Clone :: clone( & value) ) )
196+ . map( Self :: #item_ident) {
197+ return :: std:: result:: Result :: Ok ( obj) ;
198+ }
199+ } ) ;
200+ } else {
201+ // one of
202+ from_json. push ( quote ! {
203+ if let :: std:: result:: Result :: Ok ( obj) = <#object_ty as #crate_name:: types:: ParseFromJSON >:: parse_from_json( :: std:: option:: Option :: Some ( :: std:: clone:: Clone :: clone( & value) ) )
204+ . map( Self :: #item_ident) {
205+ if res_obj. is_some( ) {
206+ return :: std:: result:: Result :: Err ( #crate_name:: types:: ParseError :: expected_type( value) ) ;
207+ }
208+ res_obj = Some ( obj) ;
209+ }
210+ } ) ;
211+ }
212+
213+ if args. externally_tagged {
214+ to_json. push ( quote ! {
215+ Self :: #item_ident( obj) => {
216+ let value = <#object_ty as #crate_name:: types:: ToJSON >:: to_json( obj) ;
217+ let mut wrapped = #crate_name:: __private:: serde_json:: Map :: new( ) ;
218+ wrapped. insert( :: std:: convert:: Into :: into( #mapping_name) , :: std:: option:: Option :: unwrap_or_default( value) ) ;
219+ :: std:: option:: Option :: Some ( #crate_name:: __private:: serde_json:: Value :: Object ( wrapped) )
220+ }
221+ } ) ;
222+ } else if let Some ( discriminator_name) = & discriminator_name {
223+ to_json. push ( quote ! {
224+ Self :: #item_ident( obj) => {
225+ let mut value = <#object_ty as #crate_name:: types:: ToJSON >:: to_json( obj) ;
226+ if let :: std:: option:: Option :: Some ( obj) = value. as_mut( ) . and_then( |value| value. as_object_mut( ) ) {
227+ obj. insert( :: std:: convert:: Into :: into( #discriminator_name) , :: std:: convert:: Into :: into( #mapping_name) ) ;
228+ }
229+ value
230+ }
231+ } ) ;
232+ } else {
233+ to_json. push ( quote ! {
234+ Self :: #item_ident( obj) => <#object_ty as #crate_name:: types:: ToJSON >:: to_json( obj)
235+ } ) ;
236+ }
237+
238+ mapping. push ( quote ! {
239+ ( :: std:: string:: ToString :: to_string( #mapping_name) , :: std:: format!( "#/components/schemas/{}" , #schema_name) )
240+ } ) ;
241+
242+ if args. externally_tagged {
243+ create_schemas. push ( quote ! {
244+ let schema = #crate_name:: registry:: MetaSchema {
245+ description: #description,
246+ all_of: :: std:: vec![
247+ #crate_name:: registry:: MetaSchemaRef :: Inline ( :: std:: boxed:: Box :: new( #crate_name:: registry:: MetaSchema {
248+ required: :: std:: vec![ #mapping_name] ,
249+ properties: :: std:: vec![
250+ (
251+ #mapping_name,
252+ <#object_ty as #crate_name:: types:: Type >:: schema_ref( ) ,
253+ )
254+ ] ,
255+ ..#crate_name:: registry:: MetaSchema :: new( "object" )
256+ } ) ) ,
257+ ] ,
258+ ..#crate_name:: registry:: MetaSchema :: ANY
259+ } ;
260+ registry. schemas. insert( #schema_name, schema) ;
261+ } ) ;
262+
263+ schemas. push ( quote ! {
264+ #crate_name:: registry:: MetaSchemaRef :: Reference ( #schema_name)
265+ } ) ;
266+ } else if let Some ( discriminator_name) = & args. discriminator_name {
267+ create_schemas. push ( quote ! {
268+ {
269+ fn __check_is_object_type<T : #crate_name:: types:: IsObjectType >( ) { }
270+ __check_is_object_type:: <#object_ty>( ) ;
271+ }
224272
225- registry. schemas. insert( #schema_name, schema) ;
226- } ) ;
273+ let schema = #crate_name:: registry:: MetaSchema {
274+ description: #description,
275+ all_of: :: std:: vec![
276+ #crate_name:: registry:: MetaSchemaRef :: Inline ( :: std:: boxed:: Box :: new( #crate_name:: registry:: MetaSchema {
277+ required: #required,
278+ properties: :: std:: vec![
279+ (
280+ #discriminator_name,
281+ #crate_name:: registry:: MetaSchemaRef :: Inline ( :: std:: boxed:: Box :: new(
282+ #crate_name:: registry:: MetaSchema {
283+ ty: "string" ,
284+ enum_items: :: std:: vec![ :: std:: convert:: Into :: into( #mapping_name) ] ,
285+ example: :: std:: option:: Option :: Some ( :: std:: convert:: Into :: into( #mapping_name) ) ,
286+ ..#crate_name:: registry:: MetaSchema :: ANY
287+ }
288+ ) ) ,
289+ )
290+ ] ,
291+ ..#crate_name:: registry:: MetaSchema :: new( "object" )
292+ } ) ) ,
293+ <#object_ty as #crate_name:: types:: Type >:: schema_ref( ) ,
294+ ] ,
295+ ..#crate_name:: registry:: MetaSchema :: ANY
296+ } ;
297+
298+ registry. schemas. insert( #schema_name, schema) ;
299+ } ) ;
227300
228- schemas. push ( quote ! {
229- #crate_name:: registry:: MetaSchemaRef :: Reference ( #schema_name)
230- } ) ;
231- } else {
232- schemas. push ( quote ! {
233- <#object_ty as #crate_name:: types:: Type >:: schema_ref( )
234- } ) ;
301+ schemas. push ( quote ! {
302+ #crate_name:: registry:: MetaSchemaRef :: Reference ( #schema_name)
303+ } ) ;
304+ } else {
305+ schemas. push ( quote ! {
306+ <#object_ty as #crate_name:: types:: Type >:: schema_ref( )
307+ } ) ;
308+ }
309+ }
310+ 2 .. => {
311+ return Err ( Error :: new_spanned (
312+ & variant. ident ,
313+ "Oneof (Union) does not support multiple variant fields" ,
314+ )
315+ . into ( ) ) ;
316+ }
235317 }
236318 }
237319
0 commit comments