@@ -57,9 +57,31 @@ pub fn generate_schema_doc_comment(
5757 lines. extend ( constraints. into_iter ( ) . map ( |line| format ! ( "- {}" , line) ) ) ;
5858 }
5959
60+ if let Some ( example) = format_schema_example ( schema) {
61+ if !lines. is_empty ( ) {
62+ lines. push ( String :: new ( ) ) ;
63+ }
64+ lines. push ( format ! ( "Example: `{}`" , example) ) ;
65+ }
66+
6067 generate_doc_comment_from_lines ( lines)
6168}
6269
70+ fn format_schema_example ( schema : & openapiv3:: Schema ) -> Option < String > {
71+ let example = schema. schema_data . example . as_ref ( ) ?;
72+ format_json_example ( example)
73+ }
74+
75+ pub ( crate ) fn format_json_example ( example : & serde_json:: Value ) -> Option < String > {
76+ match example {
77+ serde_json:: Value :: Null => Some ( "null" . to_string ( ) ) ,
78+ serde_json:: Value :: Bool ( value) => Some ( value. to_string ( ) ) ,
79+ serde_json:: Value :: Number ( value) => Some ( value. to_string ( ) ) ,
80+ serde_json:: Value :: String ( value) => Some ( value. clone ( ) ) ,
81+ serde_json:: Value :: Array ( _) | serde_json:: Value :: Object ( _) => None ,
82+ }
83+ }
84+
6385fn generate_doc_comment_from_lines ( lines : Vec < String > ) -> TokenStream {
6486 if lines. is_empty ( ) {
6587 return quote ! { } ;
@@ -740,6 +762,23 @@ impl<'spec, 'schemas> NestedStructGenerator<'spec, 'schemas> {
740762
741763 Ok ( ( ) )
742764 }
765+
766+ fn generate_for_string_enum (
767+ & mut self ,
768+ parent_name : & str ,
769+ field_name : & str ,
770+ schema : & openapiv3:: Schema ,
771+ enumeration : & [ Option < String > ] ,
772+ fallback_suffix : & str ,
773+ ) -> Result < ( ) , String > {
774+ let type_name = nested_inline_type_name ( parent_name, field_name, fallback_suffix) ;
775+ let type_ident = Ident :: new ( & type_name, Span :: call_site ( ) ) ;
776+ let description = schema. schema_data . description . as_deref ( ) ;
777+ let enum_tokens =
778+ generate_inline_string_enum ( & type_ident, enumeration, description, schema) ?;
779+ self . nested_schemas . push ( enum_tokens) ;
780+ Ok ( ( ) )
781+ }
743782}
744783
745784fn collect_mixin_all_of_references (
@@ -805,6 +844,17 @@ pub fn collect_nested_schemas(
805844 }
806845 generator. generate_for_object ( parent_name, field_name, schema, obj) ?;
807846 }
847+ openapiv3:: SchemaKind :: Type ( openapiv3:: Type :: String ( string_type) ) => {
848+ if !string_type. enumeration . is_empty ( ) {
849+ generator. generate_for_string_enum (
850+ parent_name,
851+ field_name,
852+ schema,
853+ & string_type. enumeration ,
854+ "" ,
855+ ) ?;
856+ }
857+ }
808858 openapiv3:: SchemaKind :: AllOf { all_of } => {
809859 if let Some ( (
810860 combined_properties,
@@ -831,6 +881,17 @@ pub fn collect_nested_schemas(
831881 openapiv3:: SchemaKind :: Type ( openapiv3:: Type :: Array ( arr) ) => {
832882 if let Some ( openapiv3:: ReferenceOr :: Item ( item_schema) ) = & arr. items {
833883 match & item_schema. schema_kind {
884+ openapiv3:: SchemaKind :: Type ( openapiv3:: Type :: String ( string_type) ) => {
885+ if !string_type. enumeration . is_empty ( ) {
886+ generator. generate_for_string_enum (
887+ parent_name,
888+ field_name,
889+ item_schema,
890+ & string_type. enumeration ,
891+ "Item" ,
892+ ) ?;
893+ }
894+ }
834895 openapiv3:: SchemaKind :: Type ( openapiv3:: Type :: Object ( obj) ) => {
835896 if should_emit_free_form_object_alias (
836897 & obj. properties ,
@@ -1222,7 +1283,15 @@ pub fn infer_rust_type(
12221283) -> TokenStream {
12231284 let base_type = match schema_kind {
12241285 openapiv3:: SchemaKind :: Type ( openapiv3:: Type :: String ( string_type) ) => {
1225- if let Some ( kind) = string_encoded_numeric_kind ( & string_type. format ) {
1286+ if !string_type. enumeration . is_empty ( ) {
1287+ if let Some ( ( parent_name, field_name) ) = parent_field {
1288+ let type_name = nested_inline_type_name ( parent_name, field_name, "" ) ;
1289+ let type_ident = Ident :: new ( & type_name, Span :: call_site ( ) ) ;
1290+ quote ! { #type_ident }
1291+ } else {
1292+ quote ! { String }
1293+ }
1294+ } else if let Some ( kind) = string_encoded_numeric_kind ( & string_type. format ) {
12261295 numeric_kind_rust_type ( kind)
12271296 } else {
12281297 match & string_type. format {
@@ -1278,6 +1347,21 @@ pub fn infer_rust_type(
12781347 quote ! { #type_ident }
12791348 }
12801349 openapiv3:: ReferenceOr :: Item ( schema) => match & schema. schema_kind {
1350+ openapiv3:: SchemaKind :: Type ( openapiv3:: Type :: String ( string_type) ) => {
1351+ if !string_type. enumeration . is_empty ( ) {
1352+ if let Some ( ( parent_name, field_name) ) = parent_field {
1353+ let type_name =
1354+ nested_inline_type_name ( parent_name, field_name, "Item" ) ;
1355+ let type_ident = Ident :: new ( & type_name, Span :: call_site ( ) ) ;
1356+ quote ! { #type_ident }
1357+ } else {
1358+ quote ! { String }
1359+ }
1360+ } else {
1361+ let dummy_ref = openapiv3:: ReferenceOr :: Item ( schema. clone ( ) ) ;
1362+ infer_rust_type ( & schema. schema_kind , true , false , None , & dummy_ref)
1363+ }
1364+ }
12811365 openapiv3:: SchemaKind :: Type ( openapiv3:: Type :: Object ( obj) ) => {
12821366 if should_emit_free_form_object_alias (
12831367 & obj. properties ,
@@ -1482,6 +1566,65 @@ pub fn sanitize_enum_variant(variant: &str) -> String {
14821566 }
14831567}
14841568
1569+ fn nested_inline_type_name ( parent_name : & str , field_name : & str , suffix : & str ) -> String {
1570+ format ! (
1571+ "{}{}{}" ,
1572+ parent_name. to_upper_camel_case( ) ,
1573+ field_name. to_upper_camel_case( ) ,
1574+ suffix
1575+ )
1576+ }
1577+
1578+ fn generate_inline_string_enum (
1579+ type_ident : & Ident ,
1580+ enumeration : & [ Option < String > ] ,
1581+ description : Option < & str > ,
1582+ schema : & openapiv3:: Schema ,
1583+ ) -> Result < TokenStream , String > {
1584+ let mut variant_names: HashSet < String > = HashSet :: new ( ) ;
1585+ let mut variants_tokens = Vec :: new ( ) ;
1586+
1587+ for variant in enumeration. iter ( ) . filter_map ( |value| value. as_deref ( ) ) {
1588+ let variant_name = sanitize_enum_variant ( variant) ;
1589+ if !variant_names. insert ( variant_name. clone ( ) ) {
1590+ return Err ( format ! (
1591+ "Duplicate enum variant name generated for inline enum type: {variant_name}"
1592+ ) ) ;
1593+ }
1594+
1595+ let variant_ident = Ident :: new ( & variant_name, Span :: call_site ( ) ) ;
1596+ if variant != variant_name {
1597+ variants_tokens. push ( quote ! {
1598+ #[ serde( rename = #variant) ]
1599+ #variant_ident
1600+ } ) ;
1601+ } else {
1602+ variants_tokens. push ( quote ! { #variant_ident } ) ;
1603+ }
1604+ }
1605+
1606+ if variants_tokens. is_empty ( ) {
1607+ return Ok ( quote ! { pub type #type_ident = String ; } ) ;
1608+ }
1609+
1610+ let other_variant_ident = if variant_names. contains ( "Other" ) {
1611+ Ident :: new ( "OtherValue" , Span :: call_site ( ) )
1612+ } else {
1613+ Ident :: new ( "Other" , Span :: call_site ( ) )
1614+ } ;
1615+ let description = generate_schema_doc_comment ( description, schema) ;
1616+
1617+ Ok ( quote ! {
1618+ #description
1619+ #[ derive( Debug , Clone , serde:: Serialize , serde:: Deserialize ) ]
1620+ pub enum #type_ident {
1621+ #( #variants_tokens, ) *
1622+ #[ serde( untagged) ]
1623+ #other_variant_ident( String ) ,
1624+ }
1625+ } )
1626+ }
1627+
14851628/// Generates a `std::error::Error` implementation for schemas marked as error types.
14861629fn generate_error_impl (
14871630 struct_name : & Ident ,
0 commit comments