Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix codegen for unit-only enums with a fallback variant. #108

Merged
merged 3 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

#### 🐞 Fixes

- Fixed unit-only enums with a fallback variant generating the wrong JSON schema and TypeScript
types.

#### ⚙️ Internal

- Updated dependencies.
Expand Down
51 changes: 38 additions & 13 deletions crates/schematic/src/schema/renderers/json_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,44 @@ impl SchemaRenderer<Schema> for JsonSchemaRenderer {
}

fn render_enum(&mut self, enu: &EnumType) -> RenderResult<Schema> {
let metadata = Metadata {
title: if self.options.set_field_name_as_title {
None
} else {
enu.name.clone()
},
description: enu
.description
.clone()
.map(|desc| clean_comment(desc, self.options.allow_newlines_in_description)),
..Default::default()
};

// Unit enum with a fallback variant
if enu
.variants
.as_ref()
.is_some_and(|v| v.len() != enu.values.len())
{
let mut one_of = vec![];

for field in enu.variants.as_ref().unwrap() {
if !field.hidden {
one_of.push(self.create_schema_from_field(field)?);
}
}

return Ok(Schema::Object(SchemaObject {
metadata: Some(Box::new(metadata)),
subschemas: Some(Box::new(SubschemaValidation {
one_of: Some(one_of),
..Default::default()
})),
..Default::default()
}));
}

// Unit enum with no fallback variant
let mut instance_type = InstanceType::String;
let mut enum_values = vec![];

Expand Down Expand Up @@ -242,19 +280,6 @@ impl SchemaRenderer<Schema> for JsonSchemaRenderer {
};
}

let metadata = Metadata {
title: if self.options.set_field_name_as_title {
None
} else {
enu.name.clone()
},
description: enu
.description
.clone()
.map(|desc| clean_comment(desc, self.options.allow_newlines_in_description)),
..Default::default()
};

Ok(Schema::Object(SchemaObject {
metadata: Some(Box::new(metadata)),
instance_type: Some(SingleOrVec::Single(Box::new(instance_type))),
Expand Down
27 changes: 23 additions & 4 deletions crates/schematic/src/schema/renderers/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ impl TypeScriptRenderer {

fn is_string_union_enum(&self, enu: &EnumType) -> bool {
matches!(self.options.enum_format, EnumFormat::Union)
// No variants
|| enu.variants.is_none()
// Unit enum with a fallback variant
|| enu.variants.as_ref().is_some_and(|v| v.len() != enu.values.len())
|| self.options.disable_references
}

Expand Down Expand Up @@ -138,12 +141,28 @@ impl TypeScriptRenderer {

fn render_enum_or_union(&mut self, enu: &EnumType) -> RenderResult {
if self.is_string_union_enum(enu) {
return self.render_union(&UnionType {
variants_types: enu
.values
// Map using variants instead of values (when available),
// so that the fallback variant is included
let variants_types = if let Some(variants) = &enu.variants {
variants
.iter()
.filter_map(|v| {
if v.hidden {
None
} else {
Some(Box::new(v.type_of.clone()))
}
})
.collect::<Vec<_>>()
} else {
enu.values
.iter()
.map(|v| Box::new(SchemaType::literal(v.clone())))
.collect(),
.collect::<Vec<_>>()
};

return self.render_union(&UnionType {
variants_types,
variants: enu.variants.clone(),
..Default::default()
});
Expand Down
14 changes: 14 additions & 0 deletions crates/schematic/tests/generator_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ derive_enum!(
}
);

derive_enum!(
#[derive(ConfigEnum, Default)]
pub enum FallbackEnum {
#[default]
Foo,
Bar,
Baz,
#[variant(fallback)]
Other(String),
}
);

/// Some comment.
#[derive(Clone, Config)]
pub struct AnotherConfig {
Expand All @@ -41,6 +53,7 @@ struct GenConfig {
map: HashMap<String, u64>,
/// This is a list of `enumerable` values.
enums: BasicEnum,
fallback_enum: FallbackEnum,
/// **Nested** field.
#[setting(nested)]
nested: AnotherConfig,
Expand Down Expand Up @@ -105,6 +118,7 @@ struct TemplateConfig {
/// This is an enum with a medium length description and deprecated.
#[deprecated = "Dont use enums!"]
enums: BasicEnum,
fallback_enum: FallbackEnum,
/// This is a nested struct with its own fields.
#[setting(nested)]
nested: AnotherConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ expression: "fs::read_to_string(file).unwrap()"
"datetime",
"decimal",
"enums",
"fallbackEnum",
"float32",
"float64",
"indexmap",
Expand Down Expand Up @@ -56,6 +57,14 @@ expression: "fs::read_to_string(file).unwrap()"
}
]
},
"fallbackEnum": {
"default": "foo",
"allOf": [
{
"$ref": "#/definitions/FallbackEnum"
}
]
},
"float32": {
"type": "number"
},
Expand Down Expand Up @@ -233,7 +242,23 @@ expression: "fs::read_to_string(file).unwrap()"
"bar",
"baz"
]
},
"FallbackEnum": {
"title": "FallbackEnum",
"oneOf": [
{
"const": "foo"
},
{
"const": "bar"
},
{
"const": "baz"
},
{
"type": "string"
}
]
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ expression: "fs::read_to_string(file).unwrap()"
}
]
},
"fallbackEnum": {
"default": "foo",
"allOf": [
{
"$ref": "#/definitions/FallbackEnum"
}
]
},
"float32": {
"type": "number"
},
Expand Down Expand Up @@ -204,7 +212,23 @@ expression: "fs::read_to_string(file).unwrap()"
"bar",
"baz"
]
},
"FallbackEnum": {
"title": "FallbackEnum",
"oneOf": [
{
"const": "foo"
},
{
"const": "bar"
},
{
"const": "baz"
},
{
"type": "string"
}
]
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ expression: "fs::read_to_string(file).unwrap()"
"datetime",
"decimal",
"enums",
"fallbackEnum",
"float32",
"float64",
"indexmap",
Expand Down Expand Up @@ -57,6 +58,14 @@ expression: "fs::read_to_string(file).unwrap()"
],
"markdownDescription": "This is a list of `enumerable` values."
},
"fallbackEnum": {
"default": "foo",
"allOf": [
{
"$ref": "#/definitions/FallbackEnum"
}
]
},
"float32": {
"type": "number"
},
Expand Down Expand Up @@ -235,7 +244,23 @@ expression: "fs::read_to_string(file).unwrap()"
"bar",
"baz"
]
},
"FallbackEnum": {
"title": "FallbackEnum",
"oneOf": [
{
"const": "foo"
},
{
"const": "bar"
},
{
"const": "baz"
},
{
"type": "string"
}
]
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ expression: "fs::read_to_string(file).unwrap()"
"datetime",
"decimal",
"enums",
"fallbackEnum",
"float32",
"float64",
"indexmap",
Expand Down Expand Up @@ -60,6 +61,15 @@ expression: "fs::read_to_string(file).unwrap()"
}
]
},
"fallbackEnum": {
"title": "fallbackEnum",
"default": "foo",
"allOf": [
{
"$ref": "#/definitions/FallbackEnum"
}
]
},
"float32": {
"title": "float32",
"type": "number"
Expand Down Expand Up @@ -255,7 +265,26 @@ expression: "fs::read_to_string(file).unwrap()"
"bar",
"baz"
]
},
"FallbackEnum": {
"oneOf": [
{
"title": "Foo",
"const": "foo"
},
{
"title": "Bar",
"const": "bar"
},
{
"title": "Baz",
"const": "baz"
},
{
"title": "Other",
"type": "string"
}
]
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ expression: "fs::read_to_string(file).unwrap()"
"example": 0
},

"fallbackEnum": "foo",

// This is a float thats deprecated.
// @deprecated
// "float32": 0.0,
Expand Down Expand Up @@ -80,4 +82,3 @@ expression: "fs::read_to_string(file).unwrap()"
// This is a list of strings.
"vector": []
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ expression: "fs::read_to_string(file).unwrap()"
"expandObjectPrimitive": {
"example": 0
},
"fallbackEnum": "foo",
"float64": 1.23,
"nested": {
"opt": "",
Expand All @@ -37,4 +38,3 @@ expression: "fs::read_to_string(file).unwrap()"
"string": "abc",
"vector": []
}

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ expandObject = {}

expandObjectPrimitive = { example = 0 }

fallbackEnum = "foo"

# This is a float thats deprecated.
# @deprecated
# float32 = 0.0
Expand Down Expand Up @@ -58,5 +60,3 @@ enums = "foo"
# An optional string.
# @envvar ENV_PREFIX_OPT
opt = ""


Loading