Use const instead of single-valued enum (#291)

This commit is contained in:
Graham Esau 2024-05-13 21:30:51 +01:00 committed by GitHub
parent 8c2c32bce0
commit 18300c67bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 61 additions and 131 deletions

View file

@ -53,17 +53,16 @@ impl<T: Serialize> MaybeSerializeWrapper<T> {
}
}
/// Create a schema for a unit enum
pub fn new_unit_enum(variant: &str) -> Schema {
// TODO switch from single-valued "enum" to "const"
/// Create a schema for a unit enum variant
pub fn new_unit_enum_variant(variant: &str) -> Schema {
json_schema!({
"type": "string",
"enum": [variant],
"const": variant,
})
}
/// Create a schema for an externally tagged enum
pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema {
/// Create a schema for an externally tagged enum variant
pub fn new_externally_tagged_enum_variant(variant: &str, sub_schema: Schema) -> Schema {
json_schema!({
"type": "object",
"properties": {
@ -74,7 +73,8 @@ pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema {
})
}
pub fn apply_internal_enum_tag(
/// Update a schema for an internally tagged enum variant
pub fn apply_internal_enum_variant_tag(
schema: &mut Schema,
tag_name: &str,
variant: &str,
@ -94,8 +94,7 @@ pub fn apply_internal_enum_tag(
tag_name.to_string(),
json!({
"type": "string",
// TODO switch from single-valued "enum" to "const"
"enum": [variant]
"const": variant
}),
);
}
@ -113,34 +112,6 @@ pub fn apply_internal_enum_tag(
}
}
/// Create a schema for an internally tagged enum
pub fn new_internally_tagged_enum(
tag_name: &str,
variant: &str,
deny_unknown_fields: bool,
) -> Schema {
// TODO switch from single-valued "enum" to "const"
let mut schema = json_schema!({
"type": "object",
"properties": {
tag_name: {
"type": "string",
"enum": [variant],
}
},
"required": [tag_name],
});
if deny_unknown_fields {
schema
.as_object_mut()
.unwrap()
.insert("additionalProperties".into(), false.into());
}
schema
}
pub fn insert_object_property<T: ?Sized + JsonSchema>(
schema: &mut Schema,
key: &str,

View file

@ -97,6 +97,7 @@ impl SchemaSettings {
skip_additional_properties: true,
}),
Box::new(SetSingleExample),
Box::new(ReplaceConstValue),
],
inline_subschemas: false,
}

View file

@ -169,3 +169,21 @@ impl Visitor for SetSingleExample {
}
}
}
/// This visitor will replace the `const` schema property with a single-valued `enum` property.
///
/// This is useful for dialects of JSON Schema (e.g. OpenAPI 3.0) that do not support the `const` property.
#[derive(Debug, Clone)]
pub struct ReplaceConstValue;
impl Visitor for ReplaceConstValue {
fn visit_schema(&mut self, schema: &mut Schema) {
visit_schema(self, schema);
if let Some(obj) = schema.as_object_mut() {
if let Some(value) = obj.remove("const") {
obj.insert("enum".into(), Value::Array(vec![value]));
}
}
}
}

View file

@ -11,10 +11,8 @@
},
{
"type": "string",
"deprecated": true,
"enum": [
"DeprecatedUnitVariant"
]
"const": "DeprecatedUnitVariant",
"deprecated": true
},
{
"type": "object",

View file

@ -13,9 +13,7 @@
{
"description": "This comment is included in the generated schema :)",
"type": "string",
"enum": [
"DocumentedUnit"
]
"const": "DocumentedUnit"
},
{
"title": "Complex variant",

View file

@ -7,9 +7,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"UnitOne"
]
"const": "UnitOne"
}
},
"additionalProperties": false,
@ -22,9 +20,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"StringMap"
]
"const": "StringMap"
}
},
"additionalProperties": {
@ -39,9 +35,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"UnitStructNewType"
]
"const": "UnitStructNewType"
}
},
"additionalProperties": false,
@ -61,9 +55,7 @@
},
"typeProperty": {
"type": "string",
"enum": [
"StructNewType"
]
"const": "StructNewType"
}
},
"required": [
@ -84,9 +76,7 @@
},
"typeProperty": {
"type": "string",
"enum": [
"Struct"
]
"const": "Struct"
}
},
"additionalProperties": false,
@ -101,9 +91,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"UnitTwo"
]
"const": "UnitTwo"
}
},
"additionalProperties": false,
@ -117,9 +105,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"WithInt"
]
"const": "WithInt"
}
},
"required": [

View file

@ -7,9 +7,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"UnitOne"
]
"const": "UnitOne"
}
},
"required": [
@ -21,9 +19,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"StringMap"
]
"const": "StringMap"
}
},
"additionalProperties": {
@ -38,9 +34,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"UnitStructNewType"
]
"const": "UnitStructNewType"
}
},
"required": [
@ -59,9 +53,7 @@
},
"typeProperty": {
"type": "string",
"enum": [
"StructNewType"
]
"const": "StructNewType"
}
},
"required": [
@ -82,9 +74,7 @@
},
"typeProperty": {
"type": "string",
"enum": [
"Struct"
]
"const": "Struct"
}
},
"required": [
@ -98,9 +88,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"UnitTwo"
]
"const": "UnitTwo"
}
},
"required": [
@ -113,9 +101,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"WithInt"
]
"const": "WithInt"
}
},
"required": [

View file

@ -7,9 +7,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"A"
]
"const": "A"
}
},
"additionalProperties": false,
@ -22,9 +20,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"B"
]
"const": "B"
}
},
"additionalProperties": false,
@ -37,9 +33,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"C"
]
"const": "C"
}
},
"additionalProperties": false,

View file

@ -7,9 +7,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"A"
]
"const": "A"
}
},
"required": [
@ -21,9 +19,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"B"
]
"const": "B"
}
},
"required": [
@ -35,9 +31,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"C"
]
"const": "C"
}
},
"required": [

View file

@ -6,23 +6,17 @@
"title": "A deer",
"description": "A female deer",
"type": "string",
"enum": [
"Do"
]
"const": "Do"
},
{
"description": "A drop of golden sun",
"type": "string",
"enum": [
"Re"
]
"const": "Re"
},
{
"description": "A name I call myself",
"type": "string",
"enum": [
"Mi"
]
"const": "Mi"
}
]
}

View file

@ -45,9 +45,7 @@
{
"description": "This is a documented unit variant",
"type": "string",
"enum": [
"DocumentedUnit"
]
"const": "DocumentedUnit"
},
{
"type": "object",

View file

@ -45,9 +45,7 @@
{
"description": "This is a documented unit variant",
"type": "string",
"enum": [
"DocumentedUnit"
]
"const": "DocumentedUnit"
},
{
"type": "object",

View file

@ -10,9 +10,7 @@
},
"typeProperty": {
"type": "string",
"enum": [
"Struct"
]
"const": "Struct"
}
},
"required": [
@ -25,9 +23,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"NewType"
]
"const": "NewType"
}
},
"required": [
@ -39,9 +35,7 @@
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"Unit"
]
"const": "Unit"
}
},
"required": [

View file

@ -197,12 +197,12 @@ fn expr_for_external_tagged_enum<'a>(
let mut schema_expr = if variant.is_unit() && variant.attrs.with.is_none() {
quote! {
schemars::_private::new_unit_enum(#name)
schemars::_private::new_unit_enum_variant(#name)
}
} else {
let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields);
quote! {
schemars::_private::new_externally_tagged_enum(#name, #sub_schema)
schemars::_private::new_externally_tagged_enum_variant(#name, #sub_schema)
}
};
@ -236,7 +236,7 @@ fn expr_for_internal_tagged_enum<'a>(
quote!({
let mut schema = #schema_expr;
schemars::_private::apply_internal_enum_tag(&mut schema, #tag_name, #name, #deny_unknown_fields);
schemars::_private::apply_internal_enum_variant_tag(&mut schema, #tag_name, #name, #deny_unknown_fields);
schema
})
})