From f5393c7af3a9917ee8a0c639d2a615509207949a Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 5 May 2024 21:36:12 +0100 Subject: [PATCH] Move new functions to `_private` They are likely to be moved/changed when the structure of `Schema` changes, so I really don't want them to be part of the public API --- schemars/src/_private.rs | 119 +++++++++++++++++++++++++++- schemars/src/schema.rs | 117 +-------------------------- schemars_derive/src/metadata.rs | 17 ++-- schemars_derive/src/schema_exprs.rs | 8 +- 4 files changed, 131 insertions(+), 130 deletions(-) diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index aa6b570a..3d599dab 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -1,7 +1,7 @@ use crate::flatten::Merge; use crate::gen::SchemaGenerator; -use crate::schema::{Metadata, Schema, SchemaObject}; -use crate::JsonSchema; +use crate::schema::{InstanceType, Metadata, ObjectValidation, Schema, SchemaObject}; +use crate::{JsonSchema, Map, Set}; use serde::Serialize; use serde_json::Value; @@ -65,3 +65,118 @@ impl MaybeSerializeWrapper { serde_json::value::to_value(self.0).ok() } } + +/// Create a schema for a unit enum +pub fn new_unit_enum(variant: &str) -> Schema { + Schema::Object(SchemaObject { + instance_type: Some(InstanceType::String.into()), + enum_values: Some(vec![variant.into()]), + ..SchemaObject::default() + }) +} + +/// Create a schema for an externally tagged enum +pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema { + Schema::Object(SchemaObject { + instance_type: Some(InstanceType::Object.into()), + object: Some(Box::new(ObjectValidation { + properties: { + let mut props = Map::new(); + props.insert(variant.to_owned(), sub_schema); + props + }, + required: { + let mut required = Set::new(); + required.insert(variant.to_owned()); + required + }, + // Externally tagged variants must prohibit additional + // properties irrespective of the disposition of + // `deny_unknown_fields`. If additional properties were allowed + // one could easily construct an object that validated against + // multiple variants since here it's the properties rather than + // the values of a property that distingish between variants. + additional_properties: Some(Box::new(false.into())), + ..Default::default() + })), + ..SchemaObject::default() + }) +} + +/// Create a schema for an internally tagged enum +pub fn new_internally_tagged_enum( + tag_name: &str, + variant: &str, + deny_unknown_fields: bool, +) -> Schema { + let tag_schema = Schema::Object(SchemaObject { + instance_type: Some(InstanceType::String.into()), + enum_values: Some(vec![variant.into()]), + ..Default::default() + }); + Schema::Object(SchemaObject { + instance_type: Some(InstanceType::Object.into()), + object: Some(Box::new(ObjectValidation { + properties: { + let mut props = Map::new(); + props.insert(tag_name.to_owned(), tag_schema); + props + }, + required: { + let mut required = Set::new(); + required.insert(tag_name.to_owned()); + required + }, + additional_properties: deny_unknown_fields.then(|| Box::new(false.into())), + ..Default::default() + })), + ..SchemaObject::default() + }) +} + +pub fn insert_object_property( + obj: &mut ObjectValidation, + key: &str, + has_default: bool, + required: bool, + schema: Schema, +) { + obj.properties.insert(key.to_owned(), schema); + if required || !(has_default || T::_schemars_private_is_option()) { + obj.required.insert(key.to_owned()); + } +} + +pub mod metadata { + use crate::Schema; + use serde_json::Value; + + macro_rules! add_metadata_fn { + ($method:ident, $name:ident, $ty:ty) => { + pub fn $method(schema: Schema, $name: impl Into<$ty>) -> Schema { + let value = $name.into(); + if value == <$ty>::default() { + schema + } else { + let mut schema_obj = schema.into_object(); + schema_obj.metadata().$name = value.into(); + Schema::Object(schema_obj) + } + } + }; + } + + add_metadata_fn!(add_description, description, String); + add_metadata_fn!(add_id, id, String); + add_metadata_fn!(add_title, title, String); + add_metadata_fn!(add_deprecated, deprecated, bool); + add_metadata_fn!(add_read_only, read_only, bool); + add_metadata_fn!(add_write_only, write_only, bool); + add_metadata_fn!(add_default, default, Value); + + pub fn add_examples>(schema: Schema, examples: I) -> Schema { + let mut schema_obj = schema.into_object(); + schema_obj.metadata().examples.extend(examples); + Schema::Object(schema_obj) + } +} diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 464f055d..4bef66e0 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -4,6 +4,7 @@ JSON Schema types. #[cfg(feature = "impl_json_schema")] use crate as schemars; +#[cfg(feature = "impl_json_schema")] use crate::JsonSchema; use crate::{Map, Set}; use serde::{Deserialize, Serialize}; @@ -25,32 +26,6 @@ pub enum Schema { Object(SchemaObject), } -macro_rules! with_metadata_fn { - ($method:ident, $name:ident, $ty:ty) => { - with_metadata_fn!( - concat!( - "Returns the schema with the ", stringify!($name), " metadata field set." - ), - $method, - $name, - $ty - ); - }; - ($doc:expr, $method:ident, $name:ident, $ty:ty) => { - #[doc = $doc] - pub fn $method(self, $name: impl Into<$ty>) -> Self { - let value = $name.into(); - if value == <$ty>::default() { - self - } else { - let mut schema_obj = self.into_object(); - schema_obj.metadata().$name = value.into(); - Schema::Object(schema_obj) - } - } - }; -} - impl Schema { /// Creates a new `$ref` schema. /// @@ -96,86 +71,6 @@ impl Schema { }, } } - - with_metadata_fn!(with_description, description, String); - with_metadata_fn!(with_id, id, String); - with_metadata_fn!(with_title, title, String); - with_metadata_fn!(with_deprecated, deprecated, bool); - with_metadata_fn!(with_read_only, read_only, bool); - with_metadata_fn!(with_write_only, write_only, bool); - with_metadata_fn!(with_default, default, Value); - - pub fn with_examples>(self, examples: I) -> Self { - let mut schema_obj = self.into_object(); - schema_obj.metadata().examples.extend(examples); - Schema::Object(schema_obj) - } - - /// Create a schema for a unit enum - pub fn new_unit_enum(variant: &str) -> Self { - Self::Object(SchemaObject { - instance_type: Some(InstanceType::String.into()), - enum_values: Some(vec![variant.into()]), - ..SchemaObject::default() - }) - } - - /// Create a schema for an externally tagged enum - pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Self { - Self::Object(SchemaObject { - instance_type: Some(InstanceType::Object.into()), - object: Some(Box::new(ObjectValidation { - properties: { - let mut props = Map::new(); - props.insert(variant.to_owned(), sub_schema); - props - }, - required: { - let mut required = Set::new(); - required.insert(variant.to_owned()); - required - }, - // Externally tagged variants must prohibit additional - // properties irrespective of the disposition of - // `deny_unknown_fields`. If additional properties were allowed - // one could easily construct an object that validated against - // multiple variants since here it's the properties rather than - // the values of a property that distingish between variants. - additional_properties: Some(Box::new(false.into())), - ..Default::default() - })), - ..SchemaObject::default() - }) - } - - /// Create a schema for an internally tagged enum - pub fn new_internally_tagged_enum(tag_name: &str, variant: &str, deny_unknown_fields: bool) -> Self { - let tag_schema = Schema::Object(SchemaObject { - instance_type: Some( - InstanceType::String.into(), - ), - enum_values: Some(vec![variant.into()]), - ..Default::default() - }); - Self::Object(SchemaObject { - instance_type: Some(InstanceType::Object.into()), - object: Some(Box::new(ObjectValidation { - properties: { - let mut props = Map::new(); - props.insert(tag_name.to_owned(), tag_schema); - props - }, - required: { - let mut required = Set::new(); - required.insert(tag_name.to_owned()); - required - }, - additional_properties: deny_unknown_fields.then(|| Box::new(false.into())), - ..Default::default() - })), - ..SchemaObject::default() - }) - } } impl From for Schema { @@ -591,16 +486,6 @@ pub struct ObjectValidation { pub property_names: Option>, } -impl ObjectValidation { - pub fn insert_property(&mut self, key: &str, mut has_default: bool, required: bool, schema: Schema) { - self.properties.insert(key.to_owned(), schema); - has_default |= T::_schemars_private_is_option(); - if required || !has_default { - self.required.insert(key.to_owned()); - } - } -} - /// The possible types of values in JSON Schema documents. /// /// See [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1). diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs index 36317885..a277e0fd 100644 --- a/schemars_derive/src/metadata.rs +++ b/schemars_derive/src/metadata.rs @@ -17,7 +17,8 @@ impl<'a> SchemaMetadata<'a> { if !setters.is_empty() { *schema_expr = quote! {{ let schema = #schema_expr; - schema #(#setters)* + #(let schema = schemars::_private::metadata::#setters;)* + schema }} } } @@ -27,29 +28,29 @@ impl<'a> SchemaMetadata<'a> { if let Some(title) = &self.title { setters.push(quote! { - .with_title(#title) + add_title(schema, #title) }); } if let Some(description) = &self.description { setters.push(quote! { - .with_description(#description) + add_description(schema, #description) }); } if self.deprecated { setters.push(quote! { - .with_deprecated(true) + add_deprecated(schema, true) }); } if self.read_only { setters.push(quote! { - .with_read_only(true) + add_read_only(schema, true) }); } if self.write_only { setters.push(quote! { - .with_write_only(true) + add_write_only(schema, true) }); } @@ -60,13 +61,13 @@ impl<'a> SchemaMetadata<'a> { } }); setters.push(quote! { - .with_examples([#(#examples),*].into_iter().flatten()) + add_examples(schema, [#(#examples),*].into_iter().flatten()) }); } if let Some(default) = &self.default { setters.push(quote! { - .with_default(#default.and_then(|d| schemars::_schemars_maybe_to_value!(d))) + add_default(schema, #default.and_then(|d| schemars::_schemars_maybe_to_value!(d))) }); } diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index ef9e114d..de9ca39b 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -179,12 +179,12 @@ fn expr_for_external_tagged_enum<'a>( let mut schema_expr = if variant.is_unit() && variant.attrs.with.is_none() { quote! { - schemars::schema::Schema::new_unit_enum(#name) + schemars::_private::new_unit_enum(#name) } } else { let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields); quote! { - schemars::schema::Schema::new_externally_tagged_enum(#name, #sub_schema) + schemars::_private::new_externally_tagged_enum(#name, #sub_schema) } }; @@ -214,7 +214,7 @@ fn expr_for_internal_tagged_enum<'a>( let name = variant.name(); let mut tag_schema = quote! { - schemars::schema::Schema::new_internally_tagged_enum(#tag_name, #name, #deny_unknown_fields) + schemars::_private::new_internally_tagged_enum(#tag_name, #name, #deny_unknown_fields) }; variant.attrs.as_metadata().apply_to_schema(&mut tag_schema); @@ -477,7 +477,7 @@ fn expr_for_struct( quote! { { #type_def - object_validation.insert_property::<#ty>(#name, #has_default, #required, #schema_expr); + schemars::_private::insert_object_property::<#ty>(object_validation, #name, #has_default, #required, #schema_expr); } } })