Skip to content

Commit

Permalink
Move new functions to _private
Browse files Browse the repository at this point in the history
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
  • Loading branch information
GREsau committed May 5, 2024
1 parent c20fc29 commit f5393c7
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 130 deletions.
119 changes: 117 additions & 2 deletions schemars/src/_private.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -65,3 +65,118 @@ impl<T: Serialize> MaybeSerializeWrapper<T> {
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<T: ?Sized + JsonSchema>(
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<I: IntoIterator<Item = Value>>(schema: Schema, examples: I) -> Schema {
let mut schema_obj = schema.into_object();
schema_obj.metadata().examples.extend(examples);
Schema::Object(schema_obj)
}
}
117 changes: 1 addition & 116 deletions schemars/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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.
///
Expand Down Expand Up @@ -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<I: IntoIterator<Item = Value>>(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<SchemaObject> for Schema {
Expand Down Expand Up @@ -591,16 +486,6 @@ pub struct ObjectValidation {
pub property_names: Option<Box<Schema>>,
}

impl ObjectValidation {
pub fn insert_property<T: ?Sized + JsonSchema>(&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).
Expand Down
17 changes: 9 additions & 8 deletions schemars_derive/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}}
}
}
Expand All @@ -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)
});
}

Expand All @@ -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)))
});
}

Expand Down
8 changes: 4 additions & 4 deletions schemars_derive/src/schema_exprs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
};

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
})
Expand Down

0 comments on commit f5393c7

Please sign in to comment.