From aa8ba273bd839d7fe485b8b39f1ef680be49c946 Mon Sep 17 00:00:00 2001 From: Miles Johnson Date: Wed, 11 Oct 2023 12:18:21 -0700 Subject: [PATCH 1/2] Add impl. --- crates/macros/src/common/container.rs | 60 ++++++++++++++++++++++----- crates/macros/src/common/variant.rs | 13 ++++++ crates/macros/src/config_enum/mod.rs | 1 + crates/types/src/enums.rs | 1 + 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/crates/macros/src/common/container.rs b/crates/macros/src/common/container.rs index a402f91e..900c7a51 100644 --- a/crates/macros/src/common/container.rs +++ b/crates/macros/src/common/container.rs @@ -1,6 +1,7 @@ use crate::common::{Field, TaggedFormat, Variant}; use proc_macro2::{Ident, TokenStream}; use quote::quote; +use syn::Fields; pub enum Container<'l> { NamedStruct { fields: Vec> }, @@ -59,29 +60,66 @@ impl<'l> Container<'l> { } } Self::Enum { variants } => { + let is_all_unit_enum = variants + .iter() + .all(|v| matches!(v.value.fields, Fields::Unit)); + let variants_types = variants .iter() .filter_map(|v| { if v.is_excluded() { None } else { - Some(v.generate_schema_type(casing_format, &tagged_format)) + Some(v.generate_schema_type( + casing_format, + if is_all_unit_enum { + &TaggedFormat::Unit + } else { + &tagged_format + }, + )) } }) .collect::>(); - quote! { - let mut schema = UnionType { - name: Some(#config_name.into()), - variants_types: vec![ - #(Box::new(#variants_types)),* - ], - ..Default::default() - }; + if is_all_unit_enum { + quote! { + let mut values = vec![]; + let variants = vec![ + #(#variants_types),* + ]; - #description + for variant in &variants { + if let SchemaType::Literal(lit) = &variant.type_of { + values.push(lit.to_owned()); + } + } + + let mut schema = EnumType { + name: Some(#config_name.into()), + values, + variants: Some(variants), + ..Default::default() + }; + + #description + + SchemaType::Enum(schema) + } + } else { + quote! { + let mut schema = UnionType { + name: Some(#config_name.into()), + variants_types: vec![ + #(Box::new(#variants_types)),* + ], + ..Default::default() + }; + + #description - SchemaType::Union(schema) + SchemaType::Union(schema) + } } } } diff --git a/crates/macros/src/common/variant.rs b/crates/macros/src/common/variant.rs index e2f95ee6..ebfa4170 100644 --- a/crates/macros/src/common/variant.rs +++ b/crates/macros/src/common/variant.rs @@ -10,6 +10,8 @@ pub enum TaggedFormat { External, Internal(String), Adjacent(String, String), + // Special case for unit only enums + Unit, } // #[setting()], #[schema()] @@ -155,6 +157,17 @@ impl<'l> Variant<'l> { }; let outer = match tagged_format { + TaggedFormat::Unit => { + // This returns `SchemaField` while other branches + // return `SchemaType`. Be aware of this downstream! + quote! { + SchemaField { + name: Some(#name.into()), + type_of: #inner, + ..Default::default() + } + } + } TaggedFormat::Untagged => inner, TaggedFormat::External => { quote! { diff --git a/crates/macros/src/config_enum/mod.rs b/crates/macros/src/config_enum/mod.rs index 6f21c033..2aecb80d 100644 --- a/crates/macros/src/config_enum/mod.rs +++ b/crates/macros/src/config_enum/mod.rs @@ -183,6 +183,7 @@ pub fn macro_impl(item: TokenStream) -> TokenStream { } SchemaType::Enum(EnumType { + description: None, name: Some(#meta_name.into()), values, variants: Some(variants), diff --git a/crates/types/src/enums.rs b/crates/types/src/enums.rs index cd23cae5..24f8b49e 100644 --- a/crates/types/src/enums.rs +++ b/crates/types/src/enums.rs @@ -3,6 +3,7 @@ use crate::SchemaField; #[derive(Clone, Debug, Default)] pub struct EnumType { + pub description: Option, pub name: Option, pub values: Vec, pub variants: Option>, From 1a2c4d4e2a455831d491627c4cbccc12ece75424 Mon Sep 17 00:00:00 2001 From: Miles Johnson Date: Wed, 11 Oct 2023 12:27:07 -0700 Subject: [PATCH 2/2] Fix tests. --- CHANGELOG.md | 6 + .../src/schema/renderers/json_schema.rs | 7 + ...generator_test__json_schema__defaults.snap | 1 + ...acro_enum_test__generates_json_schema.snap | 167 ++---------------- ...macro_enum_test__generates_typescript.snap | 32 +--- .../macros_test__generates_json_schema.snap | 5 + 6 files changed, 31 insertions(+), 187 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3b0981..adba6b79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +#### 🚀 Updates + +- Updated enums with all unit variants to use the `Enum` schema type, instead of the `Union` schema + type. Enums that mix and match unit with other variants will to continue to use `Union`, and will + respect serde tagging. + #### ⚙️ Internal - Reworked dependencies and features so that some dependencies only enable when a feature is diff --git a/crates/config/src/schema/renderers/json_schema.rs b/crates/config/src/schema/renderers/json_schema.rs index 1ce24218..f39baffb 100644 --- a/crates/config/src/schema/renderers/json_schema.rs +++ b/crates/config/src/schema/renderers/json_schema.rs @@ -137,7 +137,14 @@ impl SchemaRenderer for JsonSchemaRenderer { }; } + let metadata = Metadata { + title: enu.name.clone(), + description: enu.description.clone().map(clean_comment), + ..Default::default() + }; + Ok(Schema::Object(SchemaObject { + metadata: Some(Box::new(metadata)), instance_type: Some(SingleOrVec::Single(Box::new(instance_type))), enum_values: Some(enum_values), ..Default::default() diff --git a/crates/config/tests/snapshots/generator_test__json_schema__defaults.snap b/crates/config/tests/snapshots/generator_test__json_schema__defaults.snap index 6e19ebed..97f4d1e4 100644 --- a/crates/config/tests/snapshots/generator_test__json_schema__defaults.snap +++ b/crates/config/tests/snapshots/generator_test__json_schema__defaults.snap @@ -177,6 +177,7 @@ expression: "fs::read_to_string(file).unwrap()" "additionalProperties": false }, "BasicEnum": { + "title": "BasicEnum", "type": "string", "enum": [ "foo", diff --git a/crates/config/tests/snapshots/macro_enum_test__generates_json_schema.snap b/crates/config/tests/snapshots/macro_enum_test__generates_json_schema.snap index 0dca4f2e..ff08feb9 100644 --- a/crates/config/tests/snapshots/macro_enum_test__generates_json_schema.snap +++ b/crates/config/tests/snapshots/macro_enum_test__generates_json_schema.snap @@ -157,43 +157,11 @@ expression: "std::fs::read_to_string(file).unwrap()" }, "AllUnit": { "title": "AllUnit", - "anyOf": [ - { - "type": "object", - "required": [ - "foo" - ], - "properties": { - "foo": { - "const": "foo" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "bar" - ], - "properties": { - "bar": { - "const": "bar" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baz" - ], - "properties": { - "baz": { - "const": "baz" - } - }, - "additionalProperties": false - } + "type": "string", + "enum": [ + "foo", + "bar", + "baz" ] }, "AllUnnamed": { @@ -405,47 +373,6 @@ expression: "std::fs::read_to_string(file).unwrap()" } ] }, - "PartialAllUnit": { - "title": "PartialAllUnit", - "anyOf": [ - { - "type": "object", - "required": [ - "foo" - ], - "properties": { - "foo": { - "const": "foo" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "bar" - ], - "properties": { - "bar": { - "const": "bar" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baz" - ], - "properties": { - "baz": { - "const": "baz" - } - }, - "additionalProperties": false - } - ] - }, "PartialAllUnnamed": { "title": "PartialAllUnnamed", "anyOf": [ @@ -713,48 +640,6 @@ expression: "std::fs::read_to_string(file).unwrap()" } ] }, - "PartialWithComments": { - "title": "PartialWithComments", - "description": "Container", - "anyOf": [ - { - "type": "object", - "required": [ - "foo" - ], - "properties": { - "foo": { - "const": "foo" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "bar" - ], - "properties": { - "bar": { - "const": "bar" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baz" - ], - "properties": { - "baz": { - "const": "baz" - } - }, - "additionalProperties": false - } - ] - }, "PartialWithSerde": { "title": "PartialWithSerde", "anyOf": [ @@ -816,43 +701,11 @@ expression: "std::fs::read_to_string(file).unwrap()" "WithComments": { "title": "WithComments", "description": "Container", - "anyOf": [ - { - "type": "object", - "required": [ - "foo" - ], - "properties": { - "foo": { - "const": "foo" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "bar" - ], - "properties": { - "bar": { - "const": "bar" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baz" - ], - "properties": { - "baz": { - "const": "baz" - } - }, - "additionalProperties": false - } + "type": "string", + "enum": [ + "foo", + "bar", + "baz" ] }, "WithSerde": { diff --git a/crates/config/tests/snapshots/macro_enum_test__generates_typescript.snap b/crates/config/tests/snapshots/macro_enum_test__generates_typescript.snap index 5ae7552a..d17584c6 100644 --- a/crates/config/tests/snapshots/macro_enum_test__generates_typescript.snap +++ b/crates/config/tests/snapshots/macro_enum_test__generates_typescript.snap @@ -6,13 +6,7 @@ expression: "std::fs::read_to_string(file).unwrap()" /* eslint-disable */ -export type AllUnit = { - foo: 'foo'; -} | { - bar: 'bar'; -} | { - baz: 'baz'; -}; +export type AllUnit = 'foo' | 'bar' | 'baz'; export type AllUnnamed = { foo: string; @@ -43,13 +37,7 @@ export type NestedConfigs = { export type WithSerde = string | boolean | number; -export type WithComments = { - foo: 'foo'; -} | { - bar: 'bar'; -} | { - baz: 'baz'; -}; +export type WithComments = 'foo' | 'bar' | 'baz'; export type Untagged = null | boolean | [number, string] | SomeConfig; @@ -79,14 +67,6 @@ export type AdjacentTagged = { content: SomeConfig; }; -export type PartialAllUnit = { - foo: 'foo'; -} | { - bar: 'bar'; -} | { - baz: 'baz'; -}; - export type PartialAllUnnamed = { foo: string; } | { @@ -116,14 +96,6 @@ export type PartialNestedConfigs = { export type PartialWithSerde = string | boolean | number; -export type PartialWithComments = { - foo: 'foo'; -} | { - bar: 'bar'; -} | { - baz: 'baz'; -}; - export type PartialUntagged = null | boolean | [number, string] | PartialSomeConfig; export type PartialExternalTagged = { diff --git a/crates/config/tests/snapshots/macros_test__generates_json_schema.snap b/crates/config/tests/snapshots/macros_test__generates_json_schema.snap index 397c245c..166eea44 100644 --- a/crates/config/tests/snapshots/macros_test__generates_json_schema.snap +++ b/crates/config/tests/snapshots/macros_test__generates_json_schema.snap @@ -51,6 +51,7 @@ expression: "std::fs::read_to_string(file).unwrap()" "additionalProperties": false, "definitions": { "Aliased": { + "title": "Aliased", "type": "string", "enum": [ "foo", @@ -59,6 +60,7 @@ expression: "std::fs::read_to_string(file).unwrap()" ] }, "BasicEnum": { + "title": "BasicEnum", "type": "string", "enum": [ "foo", @@ -362,6 +364,7 @@ expression: "std::fs::read_to_string(file).unwrap()" "additionalProperties": false }, "OtherEnum": { + "title": "OtherEnum", "type": "string", "enum": [ "foo", @@ -751,6 +754,7 @@ expression: "std::fs::read_to_string(file).unwrap()" "additionalProperties": false }, "SomeEnum": { + "title": "SomeEnum", "type": "string", "enum": [ "a", @@ -759,6 +763,7 @@ expression: "std::fs::read_to_string(file).unwrap()" ] }, "Test": { + "title": "Test", "type": "string", "enum": [ "FOO",