chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

171
vendor/schemars/src/_private.rs vendored Normal file
View File

@@ -0,0 +1,171 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::{InstanceType, ObjectValidation, Schema, SchemaObject};
use crate::{JsonSchema, Map, Set};
use serde::Serialize;
use serde_json::Value;
// Helper for generating schemas for flattened `Option` fields.
pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
generator: &mut SchemaGenerator,
required: bool,
) -> Schema {
let mut schema = T::_schemars_private_non_optional_json_schema(generator);
if T::_schemars_private_is_option() && !required {
if let Schema::Object(SchemaObject {
object: Some(ref mut object_validation),
..
}) = schema
{
object_validation.required.clear();
}
}
schema
}
/// Hack to simulate specialization:
/// `MaybeSerializeWrapper(x).maybe_to_value()` will resolve to either
/// - The inherent method `MaybeSerializeWrapper::maybe_to_value(...)` if x is `Serialize`
/// - The trait method `NoSerialize::maybe_to_value(...)` from the blanket impl otherwise
#[doc(hidden)]
#[macro_export]
macro_rules! _schemars_maybe_to_value {
($expression:expr) => {{
#[allow(unused_imports)]
use $crate::_private::{MaybeSerializeWrapper, NoSerialize as _};
MaybeSerializeWrapper($expression).maybe_to_value()
}};
}
pub struct MaybeSerializeWrapper<T>(pub T);
pub trait NoSerialize: Sized {
fn maybe_to_value(self) -> Option<Value> {
None
}
}
impl<T> NoSerialize for T {}
impl<T: Serialize> MaybeSerializeWrapper<T> {
pub fn maybe_to_value(self) -> Option<Value> {
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 !has_default && (required || !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, Option<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)
}
}

180
vendor/schemars/src/flatten.rs vendored Normal file
View File

@@ -0,0 +1,180 @@
use crate::schema::*;
use crate::{Map, Set};
impl Schema {
/// This function is only public for use by schemars_derive.
///
/// It should not be considered part of the public API.
#[doc(hidden)]
pub fn flatten(self, other: Self) -> Schema {
if is_null_type(&self) {
return other;
} else if is_null_type(&other) {
return self;
}
let s1: SchemaObject = self.into();
let s2: SchemaObject = other.into();
Schema::Object(s1.merge(s2))
}
}
pub(crate) trait Merge: Sized {
fn merge(self, other: Self) -> Self;
}
macro_rules! impl_merge {
($ty:ident { merge: $($merge_field:ident)*, or: $($or_field:ident)*, }) => {
impl Merge for $ty {
fn merge(self, other: Self) -> Self {
$ty {
$($merge_field: self.$merge_field.merge(other.$merge_field),)*
$($or_field: self.$or_field.or(other.$or_field),)*
}
}
}
};
($ty:ident { or: $($or_field:ident)*, }) => {
impl_merge!( $ty { merge: , or: $($or_field)*, });
};
}
// For ObjectValidation::additional_properties.
impl Merge for Option<Box<Schema>> {
fn merge(self, other: Self) -> Self {
match (self.map(|x| *x), other.map(|x| *x)) {
// Perfer permissive schemas.
(Some(Schema::Bool(true)), _) => Some(Box::new(true.into())),
(_, Some(Schema::Bool(true))) => Some(Box::new(true.into())),
(None, _) => None,
(_, None) => None,
// Merge if we have two non-trivial schemas.
(Some(Schema::Object(s1)), Some(Schema::Object(s2))) => {
Some(Box::new(Schema::Object(s1.merge(s2))))
}
// Perfer the more permissive schema.
(Some(s1 @ Schema::Object(_)), Some(Schema::Bool(false))) => Some(Box::new(s1)),
(Some(Schema::Bool(false)), Some(s2 @ Schema::Object(_))) => Some(Box::new(s2)),
// Default to the null schema.
(Some(Schema::Bool(false)), Some(Schema::Bool(false))) => Some(Box::new(false.into())),
}
}
}
impl_merge!(SchemaObject {
merge: extensions instance_type enum_values
metadata subschemas number string array object,
or: format const_value reference,
});
impl Merge for Metadata {
fn merge(self, other: Self) -> Self {
Metadata {
id: self.id.or(other.id),
title: self.title.or(other.title),
description: self.description.or(other.description),
default: self.default.or(other.default),
deprecated: self.deprecated || other.deprecated,
read_only: self.read_only || other.read_only,
write_only: self.write_only || other.write_only,
examples: self.examples.merge(other.examples),
}
}
}
impl_merge!(SubschemaValidation {
or: all_of any_of one_of not if_schema then_schema else_schema,
});
impl_merge!(NumberValidation {
or: multiple_of maximum exclusive_maximum minimum exclusive_minimum,
});
impl_merge!(StringValidation {
or: max_length min_length pattern,
});
impl_merge!(ArrayValidation {
or: items additional_items max_items min_items unique_items contains,
});
impl_merge!(ObjectValidation {
merge: required properties pattern_properties additional_properties,
or: max_properties min_properties property_names,
});
impl<T: Merge> Merge for Option<T> {
fn merge(self, other: Self) -> Self {
match (self, other) {
(Some(x), Some(y)) => Some(x.merge(y)),
(None, y) => y,
(x, None) => x,
}
}
}
impl<T: Merge> Merge for Box<T> {
fn merge(mut self, other: Self) -> Self {
*self = (*self).merge(*other);
self
}
}
impl<T> Merge for Vec<T> {
fn merge(mut self, other: Self) -> Self {
self.extend(other);
self
}
}
impl<K, V> Merge for Map<K, V>
where
K: std::hash::Hash + Eq + Ord,
{
fn merge(mut self, other: Self) -> Self {
self.extend(other);
self
}
}
impl<T: Ord> Merge for Set<T> {
fn merge(mut self, other: Self) -> Self {
self.extend(other);
self
}
}
impl Merge for SingleOrVec<InstanceType> {
fn merge(self, other: Self) -> Self {
if self == other {
return self;
}
let mut vec = match (self, other) {
(SingleOrVec::Vec(v1), SingleOrVec::Vec(v2)) => v1.merge(v2),
(SingleOrVec::Vec(mut v), SingleOrVec::Single(s))
| (SingleOrVec::Single(s), SingleOrVec::Vec(mut v)) => {
v.push(*s);
v
}
(SingleOrVec::Single(s1), SingleOrVec::Single(s2)) => vec![*s1, *s2],
};
vec.sort();
vec.dedup();
SingleOrVec::Vec(vec)
}
}
fn is_null_type(schema: &Schema) -> bool {
let s = match schema {
Schema::Object(s) => s,
_ => return false,
};
let instance_type = match &s.instance_type {
Some(SingleOrVec::Single(t)) => t,
_ => return false,
};
**instance_type == InstanceType::Null
}

518
vendor/schemars/src/gen.rs vendored Normal file
View File

@@ -0,0 +1,518 @@
/*!
JSON Schema generator and settings.
This module is useful if you want more control over how the schema generated than the [`schema_for!`] macro gives you.
There are two main types in this module:
* [`SchemaSettings`], which defines what JSON Schema features should be used when generating schemas (for example, how `Option`s should be represented).
* [`SchemaGenerator`], which manages the generation of a schema document.
*/
use crate::schema::*;
use crate::{visit::*, JsonSchema, Map};
use dyn_clone::DynClone;
use serde::Serialize;
use std::borrow::Cow;
use std::collections::HashMap;
use std::{any::Any, collections::HashSet, fmt::Debug};
/// Settings to customize how Schemas are generated.
///
/// The default settings currently conform to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7), but this is liable to change in a future version of Schemars if support for other JSON Schema versions is added.
/// If you require your generated schemas to conform to draft 7, consider using the [`draft07`](#method.draft07) method.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct SchemaSettings {
/// If `true`, schemas for [`Option<T>`](Option) will include a `nullable` property.
///
/// This is not part of the JSON Schema spec, but is used in Swagger/OpenAPI schemas.
///
/// Defaults to `false`.
pub option_nullable: bool,
/// If `true`, schemas for [`Option<T>`](Option) will have `null` added to their [`type`](../schema/struct.SchemaObject.html#structfield.instance_type).
///
/// Defaults to `true`.
pub option_add_null_type: bool,
/// A JSON pointer to the expected location of referenceable subschemas within the resulting root schema.
///
/// Defaults to `"#/definitions/"`.
pub definitions_path: String,
/// The URI of the meta-schema describing the structure of the generated schemas.
///
/// Defaults to `"http://json-schema.org/draft-07/schema#"`.
pub meta_schema: Option<String>,
/// A list of visitors that get applied to all generated root schemas.
pub visitors: Vec<Box<dyn GenVisitor>>,
/// Inline all subschemas instead of using references.
///
/// Some references may still be generated in schemas for recursive types.
///
/// Defaults to `false`.
pub inline_subschemas: bool,
}
impl Default for SchemaSettings {
fn default() -> SchemaSettings {
SchemaSettings::draft07()
}
}
impl SchemaSettings {
/// Creates `SchemaSettings` that conform to [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7).
pub fn draft07() -> SchemaSettings {
SchemaSettings {
option_nullable: false,
option_add_null_type: true,
definitions_path: "#/definitions/".to_owned(),
meta_schema: Some("http://json-schema.org/draft-07/schema#".to_owned()),
visitors: vec![Box::new(RemoveRefSiblings)],
inline_subschemas: false,
}
}
/// Creates `SchemaSettings` that conform to [JSON Schema 2019-09](https://json-schema.org/specification-links.html#2019-09-formerly-known-as-draft-8).
pub fn draft2019_09() -> SchemaSettings {
SchemaSettings {
option_nullable: false,
option_add_null_type: true,
definitions_path: "#/definitions/".to_owned(),
meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()),
visitors: Vec::default(),
inline_subschemas: false,
}
}
/// Creates `SchemaSettings` that conform to [OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject).
pub fn openapi3() -> SchemaSettings {
SchemaSettings {
option_nullable: true,
option_add_null_type: false,
definitions_path: "#/components/schemas/".to_owned(),
meta_schema: Some(
"https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema"
.to_owned(),
),
visitors: vec![
Box::new(RemoveRefSiblings),
Box::new(ReplaceBoolSchemas {
skip_additional_properties: true,
}),
Box::new(SetSingleExample {
retain_examples: false,
}),
],
inline_subschemas: false,
}
}
/// Modifies the `SchemaSettings` by calling the given function.
///
/// # Example
/// ```
/// use schemars::r#gen::{SchemaGenerator, SchemaSettings};
///
/// let settings = SchemaSettings::default().with(|s| {
/// s.option_nullable = true;
/// s.option_add_null_type = false;
/// });
/// let generator = settings.into_generator();
/// ```
pub fn with(mut self, configure_fn: impl FnOnce(&mut Self)) -> Self {
configure_fn(&mut self);
self
}
/// Appends the given visitor to the list of [visitors](SchemaSettings::visitors) for these `SchemaSettings`.
pub fn with_visitor(mut self, visitor: impl Visitor + Debug + Clone + 'static) -> Self {
self.visitors.push(Box::new(visitor));
self
}
/// Creates a new [`SchemaGenerator`] using these settings.
pub fn into_generator(self) -> SchemaGenerator {
SchemaGenerator::new(self)
}
}
/// The main type used to generate JSON Schemas.
///
/// # Example
/// ```
/// use schemars::{JsonSchema, r#gen::SchemaGenerator};
///
/// #[derive(JsonSchema)]
/// struct MyStruct {
/// foo: i32,
/// }
///
/// let generator = SchemaGenerator::default();
/// let schema = generator.into_root_schema_for::<MyStruct>();
/// ```
#[derive(Debug, Default)]
pub struct SchemaGenerator {
settings: SchemaSettings,
definitions: Map<String, Schema>,
pending_schema_ids: HashSet<Cow<'static, str>>,
schema_id_to_name: HashMap<Cow<'static, str>, String>,
used_schema_names: HashSet<String>,
}
impl Clone for SchemaGenerator {
fn clone(&self) -> Self {
Self {
settings: self.settings.clone(),
definitions: self.definitions.clone(),
pending_schema_ids: HashSet::new(),
schema_id_to_name: HashMap::new(),
used_schema_names: HashSet::new(),
}
}
}
impl From<SchemaSettings> for SchemaGenerator {
fn from(settings: SchemaSettings) -> Self {
settings.into_generator()
}
}
impl SchemaGenerator {
/// Creates a new `SchemaGenerator` using the given settings.
pub fn new(settings: SchemaSettings) -> SchemaGenerator {
SchemaGenerator {
settings,
..Default::default()
}
}
/// Borrows the [`SchemaSettings`] being used by this `SchemaGenerator`.
///
/// # Example
/// ```
/// use schemars::r#gen::SchemaGenerator;
///
/// let generator = SchemaGenerator::default();
/// let settings = generator.settings();
///
/// assert_eq!(settings.option_add_null_type, true);
/// ```
pub fn settings(&self) -> &SchemaSettings {
&self.settings
}
#[deprecated = "This method no longer has any effect."]
pub fn make_extensible(&self, _schema: &mut SchemaObject) {}
#[deprecated = "Use `Schema::Bool(true)` instead"]
pub fn schema_for_any(&self) -> Schema {
Schema::Bool(true)
}
#[deprecated = "Use `Schema::Bool(false)` instead"]
pub fn schema_for_none(&self) -> Schema {
Schema::Bool(false)
}
/// Generates a JSON Schema for the type `T`, and returns either the schema itself or a `$ref` schema referencing `T`'s schema.
///
/// If `T` is [referenceable](JsonSchema::is_referenceable), this will add `T`'s schema to this generator's definitions, and
/// return a `$ref` schema referencing that schema. Otherwise, this method behaves identically to [`JsonSchema::json_schema`].
///
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
/// add them to the `SchemaGenerator`'s schema definitions.
pub fn subschema_for<T: ?Sized + JsonSchema>(&mut self) -> Schema {
let id = T::schema_id();
let return_ref = T::is_referenceable()
&& (!self.settings.inline_subschemas || self.pending_schema_ids.contains(&id));
if return_ref {
let name = match self.schema_id_to_name.get(&id).cloned() {
Some(n) => n,
None => {
let base_name = T::schema_name();
let mut name = String::new();
if self.used_schema_names.contains(&base_name) {
for i in 2.. {
name = format!("{}{}", base_name, i);
if !self.used_schema_names.contains(&name) {
break;
}
}
} else {
name = base_name;
}
self.used_schema_names.insert(name.clone());
self.schema_id_to_name.insert(id.clone(), name.clone());
name
}
};
let reference = format!("{}{}", self.settings.definitions_path, name);
if !self.definitions.contains_key(&name) {
self.insert_new_subschema_for::<T>(name, id);
}
Schema::new_ref(reference)
} else {
self.json_schema_internal::<T>(id)
}
}
fn insert_new_subschema_for<T: ?Sized + JsonSchema>(
&mut self,
name: String,
id: Cow<'static, str>,
) {
let dummy = Schema::Bool(false);
// insert into definitions BEFORE calling json_schema to avoid infinite recursion
self.definitions.insert(name.clone(), dummy);
let schema = self.json_schema_internal::<T>(id);
self.definitions.insert(name, schema);
}
/// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated.
///
/// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas
/// themselves.
pub fn definitions(&self) -> &Map<String, Schema> {
&self.definitions
}
/// Mutably borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated.
///
/// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas
/// themselves.
pub fn definitions_mut(&mut self) -> &mut Map<String, Schema> {
&mut self.definitions
}
/// Returns the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated,
/// leaving an empty map in its place.
///
/// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas
/// themselves.
pub fn take_definitions(&mut self) -> Map<String, Schema> {
std::mem::take(&mut self.definitions)
}
/// Returns an iterator over the [visitors](SchemaSettings::visitors) being used by this `SchemaGenerator`.
pub fn visitors_mut(&mut self) -> impl Iterator<Item = &mut dyn GenVisitor> {
self.settings.visitors.iter_mut().map(|v| v.as_mut())
}
/// Generates a root JSON Schema for the type `T`.
///
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
/// add them to the `SchemaGenerator`'s schema definitions and include them in the returned `SchemaObject`'s
/// [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> RootSchema {
let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
schema.metadata().title.get_or_insert_with(T::schema_name);
let mut root = RootSchema {
meta_schema: self.settings.meta_schema.clone(),
definitions: self.definitions.clone(),
schema,
};
for visitor in &mut self.settings.visitors {
visitor.visit_root_schema(&mut root)
}
root
}
/// Consumes `self` and generates a root JSON Schema for the type `T`.
///
/// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
/// include them in the returned `SchemaObject`'s [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> RootSchema {
let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
schema.metadata().title.get_or_insert_with(T::schema_name);
let mut root = RootSchema {
meta_schema: self.settings.meta_schema,
definitions: self.definitions,
schema,
};
for visitor in &mut self.settings.visitors {
visitor.visit_root_schema(&mut root)
}
root
}
/// Generates a root JSON Schema for the given example value.
///
/// If the value implements [`JsonSchema`](crate::JsonSchema), then prefer using the [`root_schema_for()`](Self::root_schema_for())
/// function which will generally produce a more precise schema, particularly when the value contains any enums.
pub fn root_schema_for_value<T: ?Sized + Serialize>(
&mut self,
value: &T,
) -> Result<RootSchema, serde_json::Error> {
let mut schema = value
.serialize(crate::ser::Serializer {
generator: self,
include_title: true,
})?
.into_object();
if let Ok(example) = serde_json::to_value(value) {
schema.metadata().examples.push(example);
}
let mut root = RootSchema {
meta_schema: self.settings.meta_schema.clone(),
definitions: self.definitions.clone(),
schema,
};
for visitor in &mut self.settings.visitors {
visitor.visit_root_schema(&mut root)
}
Ok(root)
}
/// Consumes `self` and generates a root JSON Schema for the given example value.
///
/// If the value implements [`JsonSchema`](crate::JsonSchema), then prefer using the [`into_root_schema_for()!`](Self::into_root_schema_for())
/// function which will generally produce a more precise schema, particularly when the value contains any enums.
pub fn into_root_schema_for_value<T: ?Sized + Serialize>(
mut self,
value: &T,
) -> Result<RootSchema, serde_json::Error> {
let mut schema = value
.serialize(crate::ser::Serializer {
generator: &mut self,
include_title: true,
})?
.into_object();
if let Ok(example) = serde_json::to_value(value) {
schema.metadata().examples.push(example);
}
let mut root = RootSchema {
meta_schema: self.settings.meta_schema,
definitions: self.definitions,
schema,
};
for visitor in &mut self.settings.visitors {
visitor.visit_root_schema(&mut root)
}
Ok(root)
}
/// Attemps to find the schema that the given `schema` is referencing.
///
/// If the given `schema` has a [`$ref`](../schema/struct.SchemaObject.html#structfield.reference) property which refers
/// to another schema in `self`'s schema definitions, the referenced schema will be returned. Otherwise, returns `None`.
///
/// # Example
/// ```
/// use schemars::{JsonSchema, r#gen::SchemaGenerator};
///
/// #[derive(JsonSchema)]
/// struct MyStruct {
/// foo: i32,
/// }
///
/// let mut generator = SchemaGenerator::default();
/// let ref_schema = generator.subschema_for::<MyStruct>();
///
/// assert!(ref_schema.is_ref());
///
/// let dereferenced = generator.dereference(&ref_schema);
///
/// assert!(dereferenced.is_some());
/// assert!(!dereferenced.unwrap().is_ref());
/// assert_eq!(dereferenced, generator.definitions().get("MyStruct"));
/// ```
pub fn dereference<'a>(&'a self, schema: &Schema) -> Option<&'a Schema> {
match schema {
Schema::Object(SchemaObject {
reference: Some(ref schema_ref),
..
}) => {
let definitions_path = &self.settings().definitions_path;
if schema_ref.starts_with(definitions_path) {
let name = &schema_ref[definitions_path.len()..];
self.definitions.get(name)
} else {
None
}
}
_ => None,
}
}
fn json_schema_internal<T: ?Sized + JsonSchema>(&mut self, id: Cow<'static, str>) -> Schema {
struct PendingSchemaState<'a> {
generator: &'a mut SchemaGenerator,
id: Cow<'static, str>,
did_add: bool,
}
impl<'a> PendingSchemaState<'a> {
fn new(generator: &'a mut SchemaGenerator, id: Cow<'static, str>) -> Self {
let did_add = generator.pending_schema_ids.insert(id.clone());
Self {
generator,
id,
did_add,
}
}
}
impl Drop for PendingSchemaState<'_> {
fn drop(&mut self) {
if self.did_add {
self.generator.pending_schema_ids.remove(&self.id);
}
}
}
let pss = PendingSchemaState::new(self, id);
T::json_schema(pss.generator)
}
}
/// A [Visitor](Visitor) which implements additional traits required to be included in a [SchemaSettings].
///
/// You will rarely need to use this trait directly as it is automatically implemented for any type which implements all of:
/// - [`Visitor`]
/// - [`std::fmt::Debug`]
/// - [`std::any::Any`] (implemented for all `'static` types)
/// - [`std::clone::Clone`]
///
/// # Example
/// ```
/// use schemars::visit::Visitor;
/// use schemars::r#gen::GenVisitor;
///
/// #[derive(Debug, Clone)]
/// struct MyVisitor;
///
/// impl Visitor for MyVisitor { }
///
/// let v: &dyn GenVisitor = &MyVisitor;
/// assert!(v.as_any().is::<MyVisitor>());
/// ```
pub trait GenVisitor: Visitor + Debug + DynClone + Any {
/// Upcasts this visitor into an `Any`, which can be used to inspect and manipulate it as its concrete type.
fn as_any(&self) -> &dyn Any;
}
dyn_clone::clone_trait_object!(GenVisitor);
impl<T> GenVisitor for T
where
T: Visitor + Debug + Clone + Any,
{
fn as_any(&self) -> &dyn Any {
self
}
}

View File

@@ -0,0 +1,106 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
// Does not require T: JsonSchema.
impl<T> JsonSchema for [T; 0] {
no_ref_schema!();
fn schema_name() -> String {
"EmptyArray".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("[]")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
max_items: Some(0),
..Default::default()
})),
..Default::default()
}
.into()
}
}
macro_rules! array_impls {
($($len:tt)+) => {
$(
impl<T: JsonSchema> JsonSchema for [T; $len] {
no_ref_schema!();
fn schema_name() -> String {
format!("Array_size_{}_of_{}", $len, T::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(
format!("[{}; {}]", $len, T::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(generator.subschema_for::<T>().into()),
max_items: Some($len),
min_items: Some($len),
..Default::default()
})),
..Default::default()
}
.into()
}
}
)+
}
}
array_impls! {
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{schema_for, schema_object_for};
use pretty_assertions::assert_eq;
#[test]
fn schema_for_array() {
let schema = schema_object_for::<[i32; 8]>();
assert_eq!(
schema.instance_type,
Some(SingleOrVec::from(InstanceType::Array))
);
let array_validation = schema.array.unwrap();
assert_eq!(
array_validation.items,
Some(SingleOrVec::from(schema_for::<i32>()))
);
assert_eq!(array_validation.max_items, Some(8));
assert_eq!(array_validation.min_items, Some(8));
}
// SomeStruct does not implement JsonSchema
struct SomeStruct;
#[test]
fn schema_for_empty_array() {
let schema = schema_object_for::<[SomeStruct; 0]>();
assert_eq!(
schema.instance_type,
Some(SingleOrVec::from(InstanceType::Array))
);
let array_validation = schema.array.unwrap();
assert_eq!(array_validation.max_items, Some(0));
}
}

View File

@@ -0,0 +1,37 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use arrayvec05::{Array, ArrayString, ArrayVec};
use std::convert::TryInto;
// Do not set maxLength on the schema as that describes length in characters, but we only
// know max length in bytes.
forward_impl!((<A> JsonSchema for ArrayString<A> where A: Array<Item = u8> + Copy) => String);
impl<A: Array> JsonSchema for ArrayVec<A>
where
A::Item: JsonSchema,
{
no_ref_schema!();
fn schema_name() -> String {
format!(
"Array_up_to_size_{}_of_{}",
A::CAPACITY,
A::Item::schema_name()
)
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(generator.subschema_for::<A::Item>().into()),
max_items: A::CAPACITY.try_into().ok(),
..Default::default()
})),
..Default::default()
}
.into()
}
}

View File

@@ -0,0 +1,33 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use arrayvec07::{ArrayString, ArrayVec};
use std::convert::TryInto;
// Do not set maxLength on the schema as that describes length in characters, but we only
// know max length in bytes.
forward_impl!((<const CAP: usize> JsonSchema for ArrayString<CAP>) => String);
impl<T, const CAP: usize> JsonSchema for ArrayVec<T, CAP>
where
T: JsonSchema,
{
no_ref_schema!();
fn schema_name() -> String {
format!("Array_up_to_size_{}_of_{}", CAP, T::schema_name())
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(generator.subschema_for::<T>().into()),
max_items: CAP.try_into().ok(),
..Default::default()
})),
..Default::default()
}
.into()
}
}

View File

@@ -0,0 +1,47 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::sync::atomic::*;
forward_impl!(AtomicBool => bool);
forward_impl!(AtomicI8 => i8);
forward_impl!(AtomicI16 => i16);
forward_impl!(AtomicI32 => i32);
#[cfg(std_atomic64)]
forward_impl!(AtomicI64 => i64);
forward_impl!(AtomicIsize => isize);
forward_impl!(AtomicU8 => u8);
forward_impl!(AtomicU16 => u16);
forward_impl!(AtomicU32 => u32);
#[cfg(std_atomic64)]
forward_impl!(AtomicU64 => u64);
forward_impl!(AtomicUsize => usize);
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::schema_object_for;
use pretty_assertions::assert_eq;
#[test]
fn schema_for_atomics() {
let atomic_schema = schema_object_for::<(
AtomicBool,
AtomicI8,
AtomicI16,
AtomicI32,
AtomicI64,
AtomicIsize,
AtomicU8,
AtomicU16,
AtomicU32,
AtomicU64,
AtomicUsize,
)>();
let basic_schema =
schema_object_for::<(bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize)>();
assert_eq!(atomic_schema, basic_schema);
}
}

View File

@@ -0,0 +1,7 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use bytes::{Bytes, BytesMut};
forward_impl!((JsonSchema for Bytes) => Vec<u8>);
forward_impl!((JsonSchema for BytesMut) => Vec<u8>);

View File

@@ -0,0 +1,68 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use chrono::prelude::*;
use serde_json::json;
use std::borrow::Cow;
impl JsonSchema for Weekday {
no_ref_schema!();
fn schema_name() -> String {
"Weekday".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("chrono::Weekday")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
enum_values: Some(vec![
json!("Mon"),
json!("Tue"),
json!("Wed"),
json!("Thu"),
json!("Fri"),
json!("Sat"),
json!("Sun"),
]),
..Default::default()
}
.into()
}
}
macro_rules! formatted_string_impl {
($ty:ident, $format:literal) => {
formatted_string_impl!($ty, $format, JsonSchema for $ty);
};
($ty:ident, $format:literal, $($desc:tt)+) => {
impl $($desc)+ {
no_ref_schema!();
fn schema_name() -> String {
stringify!($ty).to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed(stringify!(chrono::$ty))
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
format: Some($format.to_owned()),
..Default::default()
}
.into()
}
}
};
}
formatted_string_impl!(NaiveDate, "date");
formatted_string_impl!(NaiveDateTime, "partial-date-time");
formatted_string_impl!(NaiveTime, "partial-date-time");
formatted_string_impl!(DateTime, "date-time", <Tz: TimeZone> JsonSchema for DateTime<Tz>);

View File

@@ -0,0 +1,230 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use serde_json::json;
use std::borrow::Cow;
use std::ops::{Bound, Range, RangeInclusive};
impl<T: JsonSchema> JsonSchema for Option<T> {
no_ref_schema!();
fn schema_name() -> String {
format!("Nullable_{}", T::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(format!("Option<{}>", T::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = generator.subschema_for::<T>();
if generator.settings().option_add_null_type {
schema = match schema {
Schema::Bool(true) => Schema::Bool(true),
Schema::Bool(false) => <()>::json_schema(generator),
Schema::Object(SchemaObject {
instance_type: Some(ref mut instance_type),
..
}) => {
add_null_type(instance_type);
schema
}
schema => SchemaObject {
// TODO technically the schema already accepts null, so this may be unnecessary
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(vec![schema, <()>::json_schema(generator)]),
..Default::default()
})),
..Default::default()
}
.into(),
}
}
if generator.settings().option_nullable {
let mut schema_obj = schema.into_object();
schema_obj
.extensions
.insert("nullable".to_owned(), json!(true));
schema = Schema::Object(schema_obj);
};
schema
}
fn _schemars_private_non_optional_json_schema(generator: &mut SchemaGenerator) -> Schema {
T::_schemars_private_non_optional_json_schema(generator)
}
fn _schemars_private_is_option() -> bool {
true
}
}
fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) {
match instance_type {
SingleOrVec::Single(ty) if **ty != InstanceType::Null => {
*instance_type = vec![**ty, InstanceType::Null].into()
}
SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => ty.push(InstanceType::Null),
_ => {}
};
}
impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
fn schema_name() -> String {
format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(format!("Result<{}, {}>", T::schema_id(), E::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut ok_schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = ok_schema.object();
obj.required.insert("Ok".to_owned());
obj.properties
.insert("Ok".to_owned(), generator.subschema_for::<T>());
let mut err_schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = err_schema.object();
obj.required.insert("Err".to_owned());
obj.properties
.insert("Err".to_owned(), generator.subschema_for::<E>());
let mut schema = SchemaObject::default();
schema.subschemas().one_of = Some(vec![ok_schema.into(), err_schema.into()]);
schema.into()
}
}
impl<T: JsonSchema> JsonSchema for Bound<T> {
fn schema_name() -> String {
format!("Bound_of_{}", T::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(format!("Bound<{}>", T::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut included_schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = included_schema.object();
obj.required.insert("Included".to_owned());
obj.properties
.insert("Included".to_owned(), generator.subschema_for::<T>());
let mut excluded_schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = excluded_schema.object();
obj.required.insert("Excluded".to_owned());
obj.properties
.insert("Excluded".to_owned(), generator.subschema_for::<T>());
let unbounded_schema = SchemaObject {
instance_type: Some(InstanceType::String.into()),
const_value: Some(json!("Unbounded")),
..Default::default()
};
let mut schema = SchemaObject::default();
schema.subschemas().one_of = Some(vec![
included_schema.into(),
excluded_schema.into(),
unbounded_schema.into(),
]);
schema.into()
}
}
impl<T: JsonSchema> JsonSchema for Range<T> {
fn schema_name() -> String {
format!("Range_of_{}", T::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(format!("Range<{}>", T::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = schema.object();
obj.required.insert("start".to_owned());
obj.required.insert("end".to_owned());
obj.properties
.insert("start".to_owned(), generator.subschema_for::<T>());
obj.properties
.insert("end".to_owned(), generator.subschema_for::<T>());
schema.into()
}
}
forward_impl!((<T: JsonSchema> JsonSchema for RangeInclusive<T>) => Range<T>);
forward_impl!((<T: ?Sized> JsonSchema for std::marker::PhantomData<T>) => ());
forward_impl!((<'a> JsonSchema for std::fmt::Arguments<'a>) => String);
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{schema_for, schema_object_for};
use pretty_assertions::assert_eq;
#[test]
fn schema_for_option() {
let schema = schema_object_for::<Option<i32>>();
assert_eq!(
schema.instance_type,
Some(vec![InstanceType::Integer, InstanceType::Null].into())
);
assert_eq!(schema.extensions.get("nullable"), None);
assert_eq!(schema.subschemas.is_none(), true);
}
#[test]
fn schema_for_option_with_ref() {
use crate as schemars;
#[derive(JsonSchema)]
struct Foo;
let schema = schema_object_for::<Option<Foo>>();
assert_eq!(schema.instance_type, None);
assert_eq!(schema.extensions.get("nullable"), None);
assert_eq!(schema.subschemas.is_some(), true);
let any_of = schema.subschemas.unwrap().any_of.unwrap();
assert_eq!(any_of.len(), 2);
assert_eq!(any_of[0], Schema::new_ref("#/definitions/Foo".to_string()));
assert_eq!(any_of[1], schema_for::<()>());
}
#[test]
fn schema_for_result() {
let schema = schema_object_for::<Result<bool, String>>();
let one_of = schema.subschemas.unwrap().one_of.unwrap();
assert_eq!(one_of.len(), 2);
let ok_schema: SchemaObject = one_of[0].clone().into();
let obj = ok_schema.object.unwrap();
assert!(obj.required.contains("Ok"));
assert_eq!(obj.properties["Ok"], schema_for::<bool>());
let err_schema: SchemaObject = one_of[1].clone().into();
let obj = err_schema.object.unwrap();
assert!(obj.required.contains("Err"));
assert_eq!(obj.properties["Err"], schema_for::<String>());
}
}

View File

@@ -0,0 +1,39 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
macro_rules! decimal_impl {
($type:ty) => {
impl JsonSchema for $type {
no_ref_schema!();
fn schema_name() -> String {
"Decimal".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("Decimal")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
string: Some(Box::new(StringValidation {
pattern: Some(r"^-?[0-9]+(\.[0-9]+)?$".to_owned()),
..Default::default()
})),
..Default::default()
}
.into()
}
}
};
}
#[cfg(feature = "rust_decimal")]
decimal_impl!(rust_decimal::Decimal);
#[cfg(feature = "bigdecimal03")]
decimal_impl!(bigdecimal03::BigDecimal);
#[cfg(feature = "bigdecimal04")]
decimal_impl!(bigdecimal04::BigDecimal);

View File

@@ -0,0 +1,30 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use either::Either;
use std::borrow::Cow;
impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
no_ref_schema!();
fn schema_name() -> String {
format!("Either_{}_or_{}", L::schema_name(), R::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(format!(
"either::Either<{}, {}>",
L::schema_id(),
R::schema_id()
))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject::default();
schema.subschemas().any_of = Some(vec![
generator.subschema_for::<L>(),
generator.subschema_for::<R>(),
]);
schema.into()
}
}

View File

@@ -0,0 +1,6 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use enumset::{EnumSet, EnumSetType};
forward_impl!((<T> JsonSchema for EnumSet<T> where T: EnumSetType + JsonSchema) => std::collections::BTreeSet<T>);

View File

@@ -0,0 +1,44 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use std::ffi::{CStr, CString, OsStr, OsString};
impl JsonSchema for OsString {
fn schema_name() -> String {
"OsString".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("std::ffi::OsString")
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut unix_schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = unix_schema.object();
obj.required.insert("Unix".to_owned());
obj.properties
.insert("Unix".to_owned(), <Vec<u8>>::json_schema(generator));
let mut win_schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = win_schema.object();
obj.required.insert("Windows".to_owned());
obj.properties
.insert("Windows".to_owned(), <Vec<u16>>::json_schema(generator));
let mut schema = SchemaObject::default();
schema.subschemas().one_of = Some(vec![unix_schema.into(), win_schema.into()]);
schema.into()
}
}
forward_impl!(OsStr => OsString);
forward_impl!(CString => Vec<u8>);
forward_impl!(CStr => Vec<u8>);

View File

@@ -0,0 +1,8 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use indexmap::{IndexMap, IndexSet};
use std::collections::{HashMap, HashSet};
forward_impl!((<K, V: JsonSchema, H> JsonSchema for IndexMap<K, V, H>) => HashMap<K, V, H>);
forward_impl!((<T: JsonSchema, H> JsonSchema for IndexSet<T, H>) => HashSet<T, H>);

View File

@@ -0,0 +1,8 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use indexmap2::{IndexMap, IndexSet};
use std::collections::{HashMap, HashSet};
forward_impl!((<K, V: JsonSchema, H> JsonSchema for IndexMap<K, V, H>) => HashMap<K, V, H>);
forward_impl!((<T: JsonSchema, H> JsonSchema for IndexSet<T, H>) => HashSet<T, H>);

View File

@@ -0,0 +1,39 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
macro_rules! map_impl {
($($desc:tt)+) => {
impl $($desc)+
where
V: JsonSchema,
{
no_ref_schema!();
fn schema_name() -> String {
format!("Map_of_{}", V::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(format!("Map<{}>", V::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let subschema = generator.subschema_for::<V>();
SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(Box::new(ObjectValidation {
additional_properties: Some(Box::new(subschema)),
..Default::default()
})),
..Default::default()
}
.into()
}
}
};
}
map_impl!(<K, V> JsonSchema for std::collections::BTreeMap<K, V>);
map_impl!(<K, V, H> JsonSchema for std::collections::HashMap<K, V, H>);

View File

@@ -0,0 +1,89 @@
macro_rules! no_ref_schema {
() => {
fn is_referenceable() -> bool {
false
}
};
}
macro_rules! forward_impl {
(($($impl:tt)+) => $target:ty) => {
impl $($impl)+ {
fn is_referenceable() -> bool {
<$target>::is_referenceable()
}
fn schema_name() -> String {
<$target>::schema_name()
}
fn schema_id() -> std::borrow::Cow<'static, str> {
<$target>::schema_id()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
<$target>::json_schema(generator)
}
fn _schemars_private_non_optional_json_schema(generator: &mut SchemaGenerator) -> Schema {
<$target>::_schemars_private_non_optional_json_schema(generator)
}
fn _schemars_private_is_option() -> bool {
<$target>::_schemars_private_is_option()
}
}
};
($ty:ty => $target:ty) => {
forward_impl!((JsonSchema for $ty) => $target);
};
}
mod array;
#[cfg(feature = "arrayvec05")]
mod arrayvec05;
#[cfg(feature = "arrayvec07")]
mod arrayvec07;
#[cfg(std_atomic)]
mod atomic;
#[cfg(feature = "bytes")]
mod bytes;
#[cfg(feature = "chrono")]
mod chrono;
mod core;
#[cfg(any(
feature = "rust_decimal",
feature = "bigdecimal03",
feature = "bigdecimal04"
))]
mod decimal;
#[cfg(feature = "either")]
mod either;
#[cfg(feature = "enumset")]
mod enumset;
mod ffi;
#[cfg(feature = "indexmap")]
mod indexmap;
#[cfg(feature = "indexmap2")]
mod indexmap2;
mod maps;
mod nonzero_signed;
mod nonzero_unsigned;
mod primitives;
#[cfg(feature = "semver")]
mod semver;
mod sequences;
mod serdejson;
#[cfg(feature = "smallvec")]
mod smallvec;
#[cfg(feature = "smol_str")]
mod smol_str;
mod time;
mod tuple;
#[cfg(feature = "url")]
mod url;
#[cfg(feature = "uuid08")]
mod uuid08;
#[cfg(feature = "uuid1")]
mod uuid1;
mod wrapper;

View File

@@ -0,0 +1,39 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use std::num::*;
macro_rules! nonzero_unsigned_impl {
($type:ty => $primitive:ty) => {
impl JsonSchema for $type {
no_ref_schema!();
fn schema_name() -> String {
stringify!($type).to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed(stringify!(std::num::$type))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let zero_schema: Schema = SchemaObject {
const_value: Some(0.into()),
..Default::default()
}
.into();
let mut schema: SchemaObject = <$primitive>::json_schema(generator).into();
schema.subschemas().not = Some(Box::from(zero_schema));
schema.into()
}
}
};
}
nonzero_unsigned_impl!(NonZeroI8 => i8);
nonzero_unsigned_impl!(NonZeroI16 => i16);
nonzero_unsigned_impl!(NonZeroI32 => i32);
nonzero_unsigned_impl!(NonZeroI64 => i64);
nonzero_unsigned_impl!(NonZeroI128 => i128);
nonzero_unsigned_impl!(NonZeroIsize => isize);

View File

@@ -0,0 +1,49 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use std::num::*;
macro_rules! nonzero_unsigned_impl {
($type:ty => $primitive:ty) => {
impl JsonSchema for $type {
no_ref_schema!();
fn schema_name() -> String {
stringify!($type).to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed(stringify!(std::num::$type))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema: SchemaObject = <$primitive>::json_schema(generator).into();
schema.number().minimum = Some(1.0);
schema.into()
}
}
};
}
nonzero_unsigned_impl!(NonZeroU8 => u8);
nonzero_unsigned_impl!(NonZeroU16 => u16);
nonzero_unsigned_impl!(NonZeroU32 => u32);
nonzero_unsigned_impl!(NonZeroU64 => u64);
nonzero_unsigned_impl!(NonZeroU128 => u128);
nonzero_unsigned_impl!(NonZeroUsize => usize);
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::schema_object_for;
use pretty_assertions::assert_eq;
#[test]
fn schema_for_nonzero_u32() {
let schema = schema_object_for::<NonZeroU32>();
assert_eq!(schema.number.unwrap().minimum, Some(1.0));
assert_eq!(schema.instance_type, Some(InstanceType::Integer.into()));
assert_eq!(schema.format, Some("uint32".to_owned()));
}
}

View File

@@ -0,0 +1,119 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::path::{Path, PathBuf};
macro_rules! simple_impl {
($type:ty => $instance_type:ident) => {
simple_impl!($type => $instance_type, stringify!($instance_type), None);
};
($type:ty => $instance_type:ident, $format:literal) => {
simple_impl!($type => $instance_type, $format, Some($format.to_owned()));
};
($type:ty => $instance_type:ident, $name:expr, $format:expr) => {
impl JsonSchema for $type {
no_ref_schema!();
fn schema_name() -> String {
$name.to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed($name)
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::$instance_type.into()),
format: $format,
..Default::default()
}
.into()
}
}
};
}
simple_impl!(str => String);
simple_impl!(String => String);
simple_impl!(bool => Boolean);
simple_impl!(f32 => Number, "float");
simple_impl!(f64 => Number, "double");
simple_impl!(i8 => Integer, "int8");
simple_impl!(i16 => Integer, "int16");
simple_impl!(i32 => Integer, "int32");
simple_impl!(i64 => Integer, "int64");
simple_impl!(i128 => Integer, "int128");
simple_impl!(isize => Integer, "int");
simple_impl!(() => Null);
simple_impl!(Path => String);
simple_impl!(PathBuf => String);
simple_impl!(Ipv4Addr => String, "ipv4");
simple_impl!(Ipv6Addr => String, "ipv6");
simple_impl!(IpAddr => String, "ip");
simple_impl!(SocketAddr => String);
simple_impl!(SocketAddrV4 => String);
simple_impl!(SocketAddrV6 => String);
macro_rules! unsigned_impl {
($type:ty => $instance_type:ident, $format:expr) => {
impl JsonSchema for $type {
no_ref_schema!();
fn schema_name() -> String {
$format.to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed($format)
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject {
instance_type: Some(InstanceType::$instance_type.into()),
format: Some($format.to_owned()),
..Default::default()
};
schema.number().minimum = Some(0.0);
schema.into()
}
}
};
}
unsigned_impl!(u8 => Integer, "uint8");
unsigned_impl!(u16 => Integer, "uint16");
unsigned_impl!(u32 => Integer, "uint32");
unsigned_impl!(u64 => Integer, "uint64");
unsigned_impl!(u128 => Integer, "uint128");
unsigned_impl!(usize => Integer, "uint");
impl JsonSchema for char {
no_ref_schema!();
fn schema_name() -> String {
"Character".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("char")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
string: Some(Box::new(StringValidation {
min_length: Some(1),
max_length: Some(1),
..Default::default()
})),
..Default::default()
}
.into()
}
}

View File

@@ -0,0 +1,30 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use semver::Version;
use std::borrow::Cow;
impl JsonSchema for Version {
no_ref_schema!();
fn schema_name() -> String {
"Version".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("semver::Version")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
string: Some(Box::new(StringValidation {
// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
pattern: Some(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$".to_owned()),
..Default::default()
})),
..Default::default()
}
.into()
}
}

View File

@@ -0,0 +1,78 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
macro_rules! seq_impl {
($($desc:tt)+) => {
impl $($desc)+
where
T: JsonSchema,
{
no_ref_schema!();
fn schema_name() -> String {
format!("Array_of_{}", T::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(
format!("[{}]", T::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(generator.subschema_for::<T>().into()),
..Default::default()
})),
..Default::default()
}
.into()
}
}
};
}
macro_rules! set_impl {
($($desc:tt)+) => {
impl $($desc)+
where
T: JsonSchema,
{
no_ref_schema!();
fn schema_name() -> String {
format!("Set_of_{}", T::schema_name())
}
fn schema_id() -> Cow<'static, str> {
Cow::Owned(
format!("Set<{}>", T::schema_id()))
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
unique_items: Some(true),
items: Some(generator.subschema_for::<T>().into()),
..Default::default()
})),
..Default::default()
}
.into()
}
}
};
}
seq_impl!(<T> JsonSchema for std::collections::BinaryHeap<T>);
seq_impl!(<T> JsonSchema for std::collections::LinkedList<T>);
seq_impl!(<T> JsonSchema for [T]);
seq_impl!(<T> JsonSchema for Vec<T>);
seq_impl!(<T> JsonSchema for std::collections::VecDeque<T>);
set_impl!(<T> JsonSchema for std::collections::BTreeSet<T>);
set_impl!(<T, H> JsonSchema for std::collections::HashSet<T, H>);

View File

@@ -0,0 +1,47 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use serde_json::{Map, Number, Value};
use std::borrow::Cow;
use std::collections::BTreeMap;
impl JsonSchema for Value {
no_ref_schema!();
fn schema_name() -> String {
"AnyValue".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("AnyValue")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
Schema::Bool(true)
}
}
forward_impl!(Map<String, Value> => BTreeMap<String, Value>);
impl JsonSchema for Number {
no_ref_schema!();
fn schema_name() -> String {
"Number".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("Number")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::Number.into()),
..Default::default()
}
.into()
}
}
#[cfg(feature = "raw_value")]
forward_impl!(serde_json::value::RawValue => Value);

View File

@@ -0,0 +1,6 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use smallvec::{Array, SmallVec};
forward_impl!((<A: Array> JsonSchema for SmallVec<A> where A::Item: JsonSchema) => Vec<A::Item>);

View File

@@ -0,0 +1,6 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use smol_str::SmolStr;
forward_impl!(SmolStr => String);

View File

@@ -0,0 +1,57 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use std::time::{Duration, SystemTime};
impl JsonSchema for Duration {
fn schema_name() -> String {
"Duration".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("std::time::Duration")
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = schema.object();
obj.required.insert("secs".to_owned());
obj.required.insert("nanos".to_owned());
obj.properties
.insert("secs".to_owned(), <u64>::json_schema(generator));
obj.properties
.insert("nanos".to_owned(), <u32>::json_schema(generator));
schema.into()
}
}
impl JsonSchema for SystemTime {
fn schema_name() -> String {
"SystemTime".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("std::time::SystemTime")
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
..Default::default()
};
let obj = schema.object();
obj.required.insert("secs_since_epoch".to_owned());
obj.required.insert("nanos_since_epoch".to_owned());
obj.properties
.insert("secs_since_epoch".to_owned(), <u64>::json_schema(generator));
obj.properties.insert(
"nanos_since_epoch".to_owned(),
<u32>::json_schema(generator),
);
schema.into()
}
}

View File

@@ -0,0 +1,90 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
macro_rules! tuple_impls {
($($len:expr => ($($name:ident)+))+) => {
$(
impl<$($name: JsonSchema),+> JsonSchema for ($($name,)+) {
no_ref_schema!();
fn schema_name() -> String {
let mut name = "Tuple_of_".to_owned();
name.push_str(&[$($name::schema_name()),+].join("_and_"));
name
}
fn schema_id() -> Cow<'static, str> {
let mut id = "(".to_owned();
id.push_str(&[$($name::schema_id()),+].join(","));
id.push(')');
Cow::Owned(id)
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let items = vec![
$(generator.subschema_for::<$name>()),+
];
SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(items.into()),
max_items: Some($len),
min_items: Some($len),
..Default::default()
})),
..Default::default()
}
.into()
}
}
)+
}
}
tuple_impls! {
1 => (T0)
2 => (T0 T1)
3 => (T0 T1 T2)
4 => (T0 T1 T2 T3)
5 => (T0 T1 T2 T3 T4)
6 => (T0 T1 T2 T3 T4 T5)
7 => (T0 T1 T2 T3 T4 T5 T6)
8 => (T0 T1 T2 T3 T4 T5 T6 T7)
9 => (T0 T1 T2 T3 T4 T5 T6 T7 T8)
10 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9)
11 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10)
12 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11)
13 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12)
14 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13)
15 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14)
16 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{schema_for, schema_object_for};
use pretty_assertions::assert_eq;
#[test]
fn schema_for_map_any_value() {
let schema = schema_object_for::<(i32, bool)>();
assert_eq!(
schema.instance_type,
Some(SingleOrVec::from(InstanceType::Array))
);
let array_validation = schema.array.unwrap();
assert_eq!(
array_validation.items,
Some(SingleOrVec::Vec(vec![
schema_for::<i32>(),
schema_for::<bool>()
]))
);
assert_eq!(array_validation.max_items, Some(2));
assert_eq!(array_validation.min_items, Some(2));
}
}

View File

@@ -0,0 +1,26 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use url::Url;
impl JsonSchema for Url {
no_ref_schema!();
fn schema_name() -> String {
"Url".to_owned()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("url::Url")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
format: Some("uri".to_owned()),
..Default::default()
}
.into()
}
}

View File

@@ -0,0 +1,26 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use uuid08::Uuid;
impl JsonSchema for Uuid {
no_ref_schema!();
fn schema_name() -> String {
"Uuid".to_string()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("uuid::Uuid")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
format: Some("uuid".to_string()),
..Default::default()
}
.into()
}
}

View File

@@ -0,0 +1,26 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use std::borrow::Cow;
use uuid1::Uuid;
impl JsonSchema for Uuid {
no_ref_schema!();
fn schema_name() -> String {
"Uuid".to_string()
}
fn schema_id() -> Cow<'static, str> {
Cow::Borrowed("uuid::Uuid")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
format: Some("uuid".to_string()),
..Default::default()
}
.into()
}
}

View File

@@ -0,0 +1,24 @@
use crate::r#gen::SchemaGenerator;
use crate::schema::Schema;
use crate::JsonSchema;
macro_rules! wrapper_impl {
($($desc:tt)+) => {
forward_impl!(($($desc)+ where T: JsonSchema) => T);
};
}
wrapper_impl!(<'a, T: ?Sized> JsonSchema for &'a T);
wrapper_impl!(<'a, T: ?Sized> JsonSchema for &'a mut T);
wrapper_impl!(<T: ?Sized> JsonSchema for Box<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::rc::Rc<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::rc::Weak<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::sync::Arc<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::sync::Weak<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::sync::Mutex<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::sync::RwLock<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::cell::Cell<T>);
wrapper_impl!(<T: ?Sized> JsonSchema for std::cell::RefCell<T>);
wrapper_impl!(<'a, T: ?Sized + ToOwned> JsonSchema for std::borrow::Cow<'a, T>);
wrapper_impl!(<T> JsonSchema for std::num::Wrapping<T>);
wrapper_impl!(<T> JsonSchema for std::cmp::Reverse<T>);

200
vendor/schemars/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,200 @@
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
/// The map type used by schemars types.
///
/// Currently a `BTreeMap` or `IndexMap` can be used, but this may change to a different implementation
/// with a similar interface in a future version of schemars.
/// The `IndexMap` will be used when the `preserve_order` feature flag is set.
#[cfg(not(feature = "preserve_order"))]
pub type Map<K, V> = std::collections::BTreeMap<K, V>;
#[cfg(feature = "preserve_order")]
pub type Map<K, V> = indexmap::IndexMap<K, V>;
/// The set type used by schemars types.
///
/// Currently a `BTreeSet`, but this may change to a different implementation
/// with a similar interface in a future version of schemars.
pub type Set<T> = std::collections::BTreeSet<T>;
/// A view into a single entry in a map, which may either be vacant or occupied.
//
/// This is constructed from the `entry` method on `BTreeMap` or `IndexMap`,
/// depending on whether the `preserve_order` feature flag is set.
#[cfg(not(feature = "preserve_order"))]
pub type MapEntry<'a, K, V> = std::collections::btree_map::Entry<'a, K, V>;
#[cfg(feature = "preserve_order")]
pub type MapEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
mod flatten;
mod json_schema_impls;
mod ser;
#[macro_use]
mod macros;
/// This module is only public for use by `schemars_derive`. It should not need to be used by code
/// outside of `schemars`, and should not be considered part of the public API.
#[doc(hidden)]
pub mod _private;
pub mod r#gen;
pub mod schema;
pub mod visit;
pub use r#gen::SchemaGenerator;
#[cfg(feature = "schemars_derive")]
extern crate schemars_derive;
use std::borrow::Cow;
#[cfg(feature = "schemars_derive")]
pub use schemars_derive::*;
// Export serde_json so schemars_derive can use it
#[doc(hidden)]
pub use serde_json as _serde_json;
use schema::Schema;
/// A type which can be described as a JSON Schema document.
///
/// This is implemented for many Rust primitive and standard library types.
///
/// This can also be automatically derived on most custom types with `#[derive(JsonSchema)]`.
///
/// # Examples
/// Deriving an implementation:
/// ```
/// use schemars::{schema_for, JsonSchema};
///
/// #[derive(JsonSchema)]
/// struct MyStruct {
/// foo: i32,
/// }
///
/// let my_schema = schema_for!(MyStruct);
/// ```
///
/// When manually implementing `JsonSchema`, as well as determining an appropriate schema,
/// you will need to determine an appropriate name and ID for the type.
/// For non-generic types, the type name/path are suitable for this:
/// ```
/// use schemars::{r#gen::SchemaGenerator, schema::Schema, JsonSchema};
/// use std::borrow::Cow;
///
/// struct NonGenericType;
///
/// impl JsonSchema for NonGenericType {
/// fn schema_name() -> String {
/// // Exclude the module path to make the name in generated schemas clearer.
/// "NonGenericType".to_owned()
/// }
///
/// fn schema_id() -> Cow<'static, str> {
/// // Include the module, in case a type with the same name is in another module/crate
/// Cow::Borrowed(concat!(module_path!(), "::NonGenericType"))
/// }
///
/// fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
/// todo!()
/// }
/// }
///
/// assert_eq!(NonGenericType::schema_id(), <&mut NonGenericType>::schema_id());
/// ```
///
/// But generic type parameters which may affect the generated schema should typically be included in the name/ID:
/// ```
/// use schemars::{r#gen::SchemaGenerator, schema::Schema, JsonSchema};
/// use std::{borrow::Cow, marker::PhantomData};
///
/// struct GenericType<T>(PhantomData<T>);
///
/// impl<T: JsonSchema> JsonSchema for GenericType<T> {
/// fn schema_name() -> String {
/// format!("GenericType_{}", T::schema_name())
/// }
///
/// fn schema_id() -> Cow<'static, str> {
/// Cow::Owned(format!(
/// "{}::GenericType<{}>",
/// module_path!(),
/// T::schema_id()
/// ))
/// }
///
/// fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
/// todo!()
/// }
/// }
///
/// assert_eq!(<GenericType<i32>>::schema_id(), <&mut GenericType<&i32>>::schema_id());
/// ```
///
pub trait JsonSchema {
/// Whether JSON Schemas generated for this type should be re-used where possible using the `$ref` keyword.
///
/// For trivial types (such as primitives), this should return `false`. For more complex types, it should return `true`.
/// For recursive types, this **must** return `true` to prevent infinite cycles when generating schemas.
///
/// By default, this returns `true`.
fn is_referenceable() -> bool {
true
}
/// The name of the generated JSON Schema.
///
/// This is used as the title for root schemas, and the key within the root's `definitions` property for subschemas.
fn schema_name() -> String;
/// Returns a string that uniquely identifies the schema produced by this type.
///
/// This does not have to be a human-readable string, and the value will not itself be included in generated schemas.
/// If two types produce different schemas, then they **must** have different `schema_id()`s,
/// but two types that produce identical schemas should *ideally* have the same `schema_id()`.
///
/// The default implementation returns the same value as `schema_name()`.
fn schema_id() -> Cow<'static, str> {
Cow::Owned(Self::schema_name())
}
/// Generates a JSON Schema for this type.
///
/// If the returned schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
/// add them to the [`SchemaGenerator`](r#gen::SchemaGenerator)'s schema definitions.
///
/// This should not return a `$ref` schema.
fn json_schema(generator: &mut r#gen::SchemaGenerator) -> Schema;
// TODO document and bring into public API?
#[doc(hidden)]
fn _schemars_private_non_optional_json_schema(generator: &mut r#gen::SchemaGenerator) -> Schema {
Self::json_schema(generator)
}
// TODO document and bring into public API?
#[doc(hidden)]
fn _schemars_private_is_option() -> bool {
false
}
}
#[cfg(test)]
pub mod tests {
use super::*;
pub fn schema_object_for<T: JsonSchema>() -> schema::SchemaObject {
schema_object(schema_for::<T>())
}
pub fn schema_for<T: JsonSchema>() -> schema::Schema {
let mut generator = r#gen::SchemaGenerator::default();
T::json_schema(&mut generator)
}
pub fn schema_object(schema: schema::Schema) -> schema::SchemaObject {
match schema {
schema::Schema::Object(o) => o,
s => panic!("Schema was not an object: {:?}", s),
}
}
}

78
vendor/schemars/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,78 @@
/// Generates a [`RootSchema`](crate::schema::RootSchema) for the given type using default settings.
///
/// The type must implement [`JsonSchema`](crate::JsonSchema).
///
/// # Example
/// ```
/// use schemars::{schema_for, JsonSchema};
///
/// #[derive(JsonSchema)]
/// struct MyStruct {
/// foo: i32,
/// }
///
/// let my_schema = schema_for!(MyStruct);
/// ```
#[cfg(doc)]
#[macro_export]
macro_rules! schema_for {
($type:ty) => {
$crate::r#gen::SchemaGenerator::default().into_root_schema_for::<$type>()
};
}
/// Generates a [`RootSchema`](crate::schema::RootSchema) for the given type using default settings.
///
/// The type must implement [`JsonSchema`](crate::JsonSchema).
///
/// # Example
/// ```
/// use schemars::{schema_for, JsonSchema};
///
/// #[derive(JsonSchema)]
/// struct MyStruct {
/// foo: i32,
/// }
///
/// let my_schema = schema_for!(MyStruct);
/// ```
#[cfg(not(doc))]
#[macro_export]
macro_rules! schema_for {
($type:ty) => {
$crate::r#gen::SchemaGenerator::default().into_root_schema_for::<$type>()
};
($_:expr) => {
compile_error!("This argument to `schema_for!` is not a type - did you mean to use `schema_for_value!` instead?")
};
}
/// Generates a [`RootSchema`](crate::schema::RootSchema) for the given example value using default settings.
///
/// The value must implement [`Serialize`](serde::Serialize). If the value also implements [`JsonSchema`](crate::JsonSchema),
/// then prefer using the [`schema_for!`](schema_for) macro which will generally produce a more precise schema,
/// particularly when the value contains any enums.
///
/// If the `Serialize` implementation of the value decides to fail, this macro will panic.
/// For a non-panicking alternative, create a [`SchemaGenerator`](crate::r#gen::SchemaGenerator) and use
/// its [`into_root_schema_for_value`](crate::r#gen::SchemaGenerator::into_root_schema_for_value) method.
///
/// # Example
/// ```
/// use schemars::schema_for_value;
///
/// #[derive(serde::Serialize)]
/// struct MyStruct {
/// foo: i32,
/// }
///
/// let my_schema = schema_for_value!(MyStruct { foo: 123 });
/// ```
#[macro_export]
macro_rules! schema_for_value {
($value:expr) => {
$crate::r#gen::SchemaGenerator::default()
.into_root_schema_for_value(&$value)
.unwrap()
};
}

551
vendor/schemars/src/schema.rs vendored Normal file
View File

@@ -0,0 +1,551 @@
/*!
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};
use serde_json::Value;
use std::ops::Deref;
/// A JSON Schema.
#[allow(clippy::large_enum_variant)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum Schema {
/// A trivial boolean JSON Schema.
///
/// The schema `true` matches everything (always passes validation), whereas the schema `false`
/// matches nothing (always fails validation).
Bool(bool),
/// A JSON Schema object.
Object(SchemaObject),
}
impl Schema {
/// Creates a new `$ref` schema.
///
/// The given reference string should be a URI reference. This will usually be a JSON Pointer
/// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6).
pub fn new_ref(reference: String) -> Self {
SchemaObject::new_ref(reference).into()
}
/// Returns `true` if `self` is a `$ref` schema.
///
/// If `self` is a [`SchemaObject`] with `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
/// Otherwise, returns `false`.
pub fn is_ref(&self) -> bool {
match self {
Schema::Object(o) => o.is_ref(),
_ => false,
}
}
/// Converts the given schema (if it is a boolean schema) into an equivalent schema object.
///
/// If the given schema is already a schema object, this has no effect.
///
/// # Example
/// ```
/// use schemars::schema::{Schema, SchemaObject};
///
/// let bool_schema = Schema::Bool(true);
///
/// assert_eq!(bool_schema.into_object(), SchemaObject::default());
/// ```
pub fn into_object(self) -> SchemaObject {
match self {
Schema::Object(o) => o,
Schema::Bool(true) => SchemaObject::default(),
Schema::Bool(false) => SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
not: Some(Schema::Object(Default::default()).into()),
..Default::default()
})),
..Default::default()
},
}
}
}
impl From<SchemaObject> for Schema {
fn from(o: SchemaObject) -> Self {
Schema::Object(o)
}
}
impl From<bool> for Schema {
fn from(b: bool) -> Self {
Schema::Bool(b)
}
}
/// The root object of a JSON Schema document.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct RootSchema {
/// The `$schema` keyword.
///
/// See [JSON Schema 8.1.1. The "$schema" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).
#[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
pub meta_schema: Option<String>,
/// The root schema itself.
#[serde(flatten)]
pub schema: SchemaObject,
/// The `definitions` keyword.
///
/// In JSON Schema draft 2019-09 this was replaced by $defs, but in Schemars this is still
/// serialized as `definitions` for backward-compatibility.
///
/// See [JSON Schema 8.2.5. Schema Re-Use With "$defs"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5),
/// and [JSON Schema (draft 07) 9. Schema Re-Use With "definitions"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9).
#[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")]
pub definitions: Map<String, Schema>,
}
/// A JSON Schema object.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct SchemaObject {
/// Properties which annotate the [`SchemaObject`] which typically have no effect when an object is being validated against the schema.
#[serde(flatten, deserialize_with = "skip_if_default")]
pub metadata: Option<Box<Metadata>>,
/// The `type` keyword.
///
/// See [JSON Schema Validation 6.1.1. "type"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1)
/// and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub instance_type: Option<SingleOrVec<InstanceType>>,
/// The `format` keyword.
///
/// See [JSON Schema Validation 7. A Vocabulary for Semantic Content With "format"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
/// The `enum` keyword.
///
/// See [JSON Schema Validation 6.1.2. "enum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)
#[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
pub enum_values: Option<Vec<Value>>,
/// The `const` keyword.
///
/// See [JSON Schema Validation 6.1.3. "const"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)
#[serde(
rename = "const",
skip_serializing_if = "Option::is_none",
deserialize_with = "allow_null"
)]
pub const_value: Option<Value>,
/// Properties of the [`SchemaObject`] which define validation assertions in terms of other schemas.
#[serde(flatten, deserialize_with = "skip_if_default")]
pub subschemas: Option<Box<SubschemaValidation>>,
/// Properties of the [`SchemaObject`] which define validation assertions for numbers.
#[serde(flatten, deserialize_with = "skip_if_default")]
pub number: Option<Box<NumberValidation>>,
/// Properties of the [`SchemaObject`] which define validation assertions for strings.
#[serde(flatten, deserialize_with = "skip_if_default")]
pub string: Option<Box<StringValidation>>,
/// Properties of the [`SchemaObject`] which define validation assertions for arrays.
#[serde(flatten, deserialize_with = "skip_if_default")]
pub array: Option<Box<ArrayValidation>>,
/// Properties of the [`SchemaObject`] which define validation assertions for objects.
#[serde(flatten, deserialize_with = "skip_if_default")]
pub object: Option<Box<ObjectValidation>>,
/// The `$ref` keyword.
///
/// See [JSON Schema 8.2.4.1. Direct References with "$ref"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).
#[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
pub reference: Option<String>,
/// Arbitrary extra properties which are not part of the JSON Schema specification, or which `schemars` does not support.
#[serde(flatten)]
pub extensions: Map<String, Value>,
}
// Deserializing "null" to `Option<Value>` directly results in `None`,
// this function instead makes it deserialize to `Some(Value::Null)`.
fn allow_null<'de, D>(de: D) -> Result<Option<Value>, D::Error>
where
D: serde::Deserializer<'de>,
{
Value::deserialize(de).map(Option::Some)
}
fn skip_if_default<'de, D, T>(deserializer: D) -> Result<Option<Box<T>>, D::Error>
where
D: serde::Deserializer<'de>,
T: Deserialize<'de> + Default + PartialEq,
{
let value = T::deserialize(deserializer)?;
if value == T::default() {
Ok(None)
} else {
Ok(Some(Box::new(value)))
}
}
macro_rules! get_or_insert_default_fn {
($name:ident, $ret:ty) => {
get_or_insert_default_fn!(
concat!(
"Returns a mutable reference to this schema's [`",
stringify!($ret),
"`](#structfield.",
stringify!($name),
"), creating it if it was `None`."
),
$name,
$ret
);
};
($doc:expr, $name:ident, $ret:ty) => {
#[doc = $doc]
pub fn $name(&mut self) -> &mut $ret {
self.$name.get_or_insert_with(Default::default)
}
};
}
impl SchemaObject {
/// Creates a new `$ref` schema.
///
/// The given reference string should be a URI reference. This will usually be a JSON Pointer
/// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6).
pub fn new_ref(reference: String) -> Self {
SchemaObject {
reference: Some(reference),
..Default::default()
}
}
/// Returns `true` if `self` is a `$ref` schema.
///
/// If `self` has `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
/// Otherwise, returns `false`.
pub fn is_ref(&self) -> bool {
self.reference.is_some()
}
/// Returns `true` if `self` accepts values of the given type, according to the [`instance_type`](struct.SchemaObject.html#structfield.instance_type) field.
///
/// This is a basic check that always returns `true` if no `instance_type` is specified on the schema,
/// and does not check any subschemas. Because of this, both `{}` and `{"not": {}}` accept any type according
/// to this method.
pub fn has_type(&self, ty: InstanceType) -> bool {
self.instance_type
.as_ref()
.map_or(true, |x| x.contains(&ty))
}
get_or_insert_default_fn!(metadata, Metadata);
get_or_insert_default_fn!(subschemas, SubschemaValidation);
get_or_insert_default_fn!(number, NumberValidation);
get_or_insert_default_fn!(string, StringValidation);
get_or_insert_default_fn!(array, ArrayValidation);
get_or_insert_default_fn!(object, ObjectValidation);
}
impl From<Schema> for SchemaObject {
fn from(schema: Schema) -> Self {
schema.into_object()
}
}
/// Properties which annotate a [`SchemaObject`] which typically have no effect when an object is being validated against the schema.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct Metadata {
/// The `$id` keyword.
///
/// See [JSON Schema 8.2.2. The "$id" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).
#[serde(rename = "$id", skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
/// The `title` keyword.
///
/// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
/// The `description` keyword.
///
/// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// The `default` keyword.
///
/// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2).
#[serde(
skip_serializing_if = "Option::is_none",
deserialize_with = "allow_null"
)]
pub default: Option<Value>,
/// The `deprecated` keyword.
///
/// See [JSON Schema Validation 9.3. "deprecated"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).
#[serde(skip_serializing_if = "is_false")]
pub deprecated: bool,
/// The `readOnly` keyword.
///
/// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).
#[serde(skip_serializing_if = "is_false")]
pub read_only: bool,
/// The `writeOnly` keyword.
///
/// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).
#[serde(skip_serializing_if = "is_false")]
pub write_only: bool,
/// The `examples` keyword.
///
/// See [JSON Schema Validation 9.5. "examples"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).
#[serde(skip_serializing_if = "Vec::is_empty")]
pub examples: Vec<Value>,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_false(b: &bool) -> bool {
!b
}
/// Properties of a [`SchemaObject`] which define validation assertions in terms of other schemas.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct SubschemaValidation {
/// The `allOf` keyword.
///
/// See [JSON Schema 9.2.1.1. "allOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub all_of: Option<Vec<Schema>>,
/// The `anyOf` keyword.
///
/// See [JSON Schema 9.2.1.2. "anyOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub any_of: Option<Vec<Schema>>,
/// The `oneOf` keyword.
///
/// See [JSON Schema 9.2.1.3. "oneOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).
#[serde(skip_serializing_if = "Option::is_none")]
pub one_of: Option<Vec<Schema>>,
/// The `not` keyword.
///
/// See [JSON Schema 9.2.1.4. "not"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).
#[serde(skip_serializing_if = "Option::is_none")]
pub not: Option<Box<Schema>>,
/// The `if` keyword.
///
/// See [JSON Schema 9.2.2.1. "if"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).
#[serde(rename = "if", skip_serializing_if = "Option::is_none")]
pub if_schema: Option<Box<Schema>>,
/// The `then` keyword.
///
/// See [JSON Schema 9.2.2.2. "then"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).
#[serde(rename = "then", skip_serializing_if = "Option::is_none")]
pub then_schema: Option<Box<Schema>>,
/// The `else` keyword.
///
/// See [JSON Schema 9.2.2.3. "else"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).
#[serde(rename = "else", skip_serializing_if = "Option::is_none")]
pub else_schema: Option<Box<Schema>>,
}
/// Properties of a [`SchemaObject`] which define validation assertions for numbers.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct NumberValidation {
/// The `multipleOf` keyword.
///
/// See [JSON Schema Validation 6.2.1. "multipleOf"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub multiple_of: Option<f64>,
/// The `maximum` keyword.
///
/// See [JSON Schema Validation 6.2.2. "maximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub maximum: Option<f64>,
/// The `exclusiveMaximum` keyword.
///
/// See [JSON Schema Validation 6.2.3. "exclusiveMaximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).
#[serde(skip_serializing_if = "Option::is_none")]
pub exclusive_maximum: Option<f64>,
/// The `minimum` keyword.
///
/// See [JSON Schema Validation 6.2.4. "minimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).
#[serde(skip_serializing_if = "Option::is_none")]
pub minimum: Option<f64>,
/// The `exclusiveMinimum` keyword.
///
/// See [JSON Schema Validation 6.2.5. "exclusiveMinimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).
#[serde(skip_serializing_if = "Option::is_none")]
pub exclusive_minimum: Option<f64>,
}
/// Properties of a [`SchemaObject`] which define validation assertions for strings.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct StringValidation {
/// The `maxLength` keyword.
///
/// See [JSON Schema Validation 6.3.1. "maxLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub max_length: Option<u32>,
/// The `minLength` keyword.
///
/// See [JSON Schema Validation 6.3.2. "minLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub min_length: Option<u32>,
/// The `pattern` keyword.
///
/// See [JSON Schema Validation 6.3.3. "pattern"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern: Option<String>,
}
/// Properties of a [`SchemaObject`] which define validation assertions for arrays.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct ArrayValidation {
/// The `items` keyword.
///
/// See [JSON Schema 9.3.1.1. "items"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<SingleOrVec<Schema>>,
/// The `additionalItems` keyword.
///
/// See [JSON Schema 9.3.1.2. "additionalItems"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_items: Option<Box<Schema>>,
/// The `maxItems` keyword.
///
/// See [JSON Schema Validation 6.4.1. "maxItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub max_items: Option<u32>,
/// The `minItems` keyword.
///
/// See [JSON Schema Validation 6.4.2. "minItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub min_items: Option<u32>,
/// The `uniqueItems` keyword.
///
/// See [JSON Schema Validation 6.4.3. "uniqueItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).
#[serde(skip_serializing_if = "Option::is_none")]
pub unique_items: Option<bool>,
/// The `contains` keyword.
///
/// See [JSON Schema 9.3.1.4. "contains"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).
#[serde(skip_serializing_if = "Option::is_none")]
pub contains: Option<Box<Schema>>,
}
/// Properties of a [`SchemaObject`] which define validation assertions for objects.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", default)]
pub struct ObjectValidation {
/// The `maxProperties` keyword.
///
/// See [JSON Schema Validation 6.5.1. "maxProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).
#[serde(skip_serializing_if = "Option::is_none")]
pub max_properties: Option<u32>,
/// The `minProperties` keyword.
///
/// See [JSON Schema Validation 6.5.2. "minProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).
#[serde(skip_serializing_if = "Option::is_none")]
pub min_properties: Option<u32>,
/// The `required` keyword.
///
/// See [JSON Schema Validation 6.5.3. "required"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).
#[serde(skip_serializing_if = "Set::is_empty")]
pub required: Set<String>,
/// The `properties` keyword.
///
/// See [JSON Schema 9.3.2.1. "properties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).
#[serde(skip_serializing_if = "Map::is_empty")]
pub properties: Map<String, Schema>,
/// The `patternProperties` keyword.
///
/// See [JSON Schema 9.3.2.2. "patternProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).
#[serde(skip_serializing_if = "Map::is_empty")]
pub pattern_properties: Map<String, Schema>,
/// The `additionalProperties` keyword.
///
/// See [JSON Schema 9.3.2.3. "additionalProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_properties: Option<Box<Schema>>,
/// The `propertyNames` keyword.
///
/// See [JSON Schema 9.3.2.5. "propertyNames"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).
#[serde(skip_serializing_if = "Option::is_none")]
pub property_names: Option<Box<Schema>>,
}
/// 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).
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase")]
pub enum InstanceType {
Null,
Boolean,
Object,
Array,
Number,
String,
Integer,
}
/// A type which can be serialized as a single item, or multiple items.
///
/// In some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
#[serde(untagged)]
pub enum SingleOrVec<T> {
Single(Box<T>),
Vec(Vec<T>),
}
impl<T> From<T> for SingleOrVec<T> {
fn from(single: T) -> Self {
SingleOrVec::Single(Box::new(single))
}
}
impl<T> From<Vec<T>> for SingleOrVec<T> {
fn from(vec: Vec<T>) -> Self {
SingleOrVec::Vec(vec)
}
}
impl<T: PartialEq> SingleOrVec<T> {
/// Returns `true` if `self` is either a `Single` equal to `x`, or a `Vec` containing `x`.
///
/// # Examples
///
/// ```
/// use schemars::schema::SingleOrVec;
///
/// let s = SingleOrVec::from(10);
/// assert!(s.contains(&10));
/// assert!(!s.contains(&20));
///
/// let v = SingleOrVec::from(vec![10, 20]);
/// assert!(v.contains(&10));
/// assert!(v.contains(&20));
/// assert!(!v.contains(&30));
/// ```
pub fn contains(&self, x: &T) -> bool {
match self {
SingleOrVec::Single(s) => s.deref() == x,
SingleOrVec::Vec(v) => v.contains(x),
}
}
}

509
vendor/schemars/src/ser.rs vendored Normal file
View File

@@ -0,0 +1,509 @@
use crate::schema::*;
use crate::JsonSchema;
use crate::{r#gen::SchemaGenerator, Map};
use serde_json::{Error, Value};
use std::{convert::TryInto, fmt::Display};
pub(crate) struct Serializer<'a> {
pub(crate) generator: &'a mut SchemaGenerator,
pub(crate) include_title: bool,
}
pub(crate) struct SerializeSeq<'a> {
generator: &'a mut SchemaGenerator,
items: Option<Schema>,
}
pub(crate) struct SerializeTuple<'a> {
generator: &'a mut SchemaGenerator,
items: Vec<Schema>,
title: &'static str,
}
pub(crate) struct SerializeMap<'a> {
generator: &'a mut SchemaGenerator,
properties: Map<String, Schema>,
current_key: Option<String>,
title: &'static str,
}
macro_rules! forward_to_subschema_for {
($fn:ident, $ty:ty) => {
fn $fn(self, _value: $ty) -> Result<Self::Ok, Self::Error> {
Ok(self.generator.subschema_for::<$ty>())
}
};
}
macro_rules! return_instance_type {
($fn:ident, $ty:ty, $instance_type:ident) => {
fn $fn(self, _value: $ty) -> Result<Self::Ok, Self::Error> {
Ok(SchemaObject {
instance_type: Some(InstanceType::$instance_type.into()),
..Default::default()
}
.into())
}
};
}
impl<'a> serde::Serializer for Serializer<'a> {
type Ok = Schema;
type Error = Error;
type SerializeSeq = SerializeSeq<'a>;
type SerializeTuple = SerializeTuple<'a>;
type SerializeTupleStruct = SerializeTuple<'a>;
type SerializeTupleVariant = Self;
type SerializeMap = SerializeMap<'a>;
type SerializeStruct = SerializeMap<'a>;
type SerializeStructVariant = Self;
return_instance_type!(serialize_i8, i8, Integer);
return_instance_type!(serialize_i16, i16, Integer);
return_instance_type!(serialize_i32, i32, Integer);
return_instance_type!(serialize_i64, i64, Integer);
return_instance_type!(serialize_i128, i128, Integer);
return_instance_type!(serialize_u8, u8, Integer);
return_instance_type!(serialize_u16, u16, Integer);
return_instance_type!(serialize_u32, u32, Integer);
return_instance_type!(serialize_u64, u64, Integer);
return_instance_type!(serialize_u128, u128, Integer);
return_instance_type!(serialize_f32, f32, Number);
return_instance_type!(serialize_f64, f64, Number);
forward_to_subschema_for!(serialize_bool, bool);
forward_to_subschema_for!(serialize_char, char);
forward_to_subschema_for!(serialize_str, &str);
forward_to_subschema_for!(serialize_bytes, &[u8]);
fn collect_str<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: Display,
{
Ok(self.generator.subschema_for::<&str>())
}
fn collect_map<K, V, I>(self, iter: I) -> Result<Self::Ok, Self::Error>
where
K: serde::Serialize,
V: serde::Serialize,
I: IntoIterator<Item = (K, V)>,
{
let value_schema = iter
.into_iter()
.try_fold(None, |acc, (_, v)| {
if acc == Some(Schema::Bool(true)) {
return Ok(acc);
}
let schema = v.serialize(Serializer {
generator: self.generator,
include_title: false,
})?;
Ok(match &acc {
None => Some(schema),
Some(items) if items != &schema => Some(Schema::Bool(true)),
_ => acc,
})
})?
.unwrap_or(Schema::Bool(true));
Ok(SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(Box::new(ObjectValidation {
additional_properties: Some(Box::new(value_schema)),
..ObjectValidation::default()
})),
..SchemaObject::default()
}
.into())
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Ok(self.generator.subschema_for::<Option<Value>>())
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
self.serialize_none()
}
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::Serialize,
{
// FIXME nasty duplication of `impl JsonSchema for Option<T>`
fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) {
match instance_type {
SingleOrVec::Single(ty) if **ty != InstanceType::Null => {
*instance_type = vec![**ty, InstanceType::Null].into()
}
SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => {
ty.push(InstanceType::Null)
}
_ => {}
};
}
let mut schema = value.serialize(Serializer {
generator: self.generator,
include_title: false,
})?;
if self.generator.settings().option_add_null_type {
schema = match schema {
Schema::Bool(true) => Schema::Bool(true),
Schema::Bool(false) => <()>::json_schema(self.generator),
Schema::Object(SchemaObject {
instance_type: Some(ref mut instance_type),
..
}) => {
add_null_type(instance_type);
schema
}
schema => SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(vec![schema, <()>::json_schema(self.generator)]),
..Default::default()
})),
..Default::default()
}
.into(),
}
}
if self.generator.settings().option_nullable {
let mut schema_obj = schema.into_object();
schema_obj
.extensions
.insert("nullable".to_owned(), serde_json::json!(true));
schema = Schema::Object(schema_obj);
};
Ok(schema)
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Ok(self.generator.subschema_for::<()>())
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Ok(Schema::Bool(true))
}
fn serialize_newtype_struct<T: ?Sized>(
self,
name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::Serialize,
{
let include_title = self.include_title;
let mut result = value.serialize(self);
if include_title {
if let Ok(Schema::Object(ref mut object)) = result {
object.metadata().title = Some(name.to_string());
}
}
result
}
fn serialize_newtype_variant<T: ?Sized>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::Serialize,
{
Ok(Schema::Bool(true))
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(SerializeSeq {
generator: self.generator,
items: None,
})
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Ok(SerializeTuple {
generator: self.generator,
items: Vec::with_capacity(len),
title: "",
})
}
fn serialize_tuple_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
let title = if self.include_title { name } else { "" };
Ok(SerializeTuple {
generator: self.generator,
items: Vec::with_capacity(len),
title,
})
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Ok(self)
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(SerializeMap {
generator: self.generator,
properties: Map::new(),
current_key: None,
title: "",
})
}
fn serialize_struct(
self,
name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
let title = if self.include_title { name } else { "" };
Ok(SerializeMap {
generator: self.generator,
properties: Map::new(),
current_key: None,
title,
})
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(self)
}
}
impl serde::ser::SerializeTupleVariant for Serializer<'_> {
type Ok = Schema;
type Error = Error;
fn serialize_field<T: ?Sized>(&mut self, _value: &T) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(Schema::Bool(true))
}
}
impl serde::ser::SerializeStructVariant for Serializer<'_> {
type Ok = Schema;
type Error = Error;
fn serialize_field<T: ?Sized>(
&mut self,
_key: &'static str,
_value: &T,
) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(Schema::Bool(true))
}
}
impl serde::ser::SerializeSeq for SerializeSeq<'_> {
type Ok = Schema;
type Error = Error;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
if self.items != Some(Schema::Bool(true)) {
let schema = value.serialize(Serializer {
generator: self.generator,
include_title: false,
})?;
match &self.items {
None => self.items = Some(schema),
Some(items) => {
if items != &schema {
self.items = Some(Schema::Bool(true))
}
}
}
}
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let items = self.items.unwrap_or(Schema::Bool(true));
Ok(SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(items.into()),
..ArrayValidation::default()
})),
..SchemaObject::default()
}
.into())
}
}
impl serde::ser::SerializeTuple for SerializeTuple<'_> {
type Ok = Schema;
type Error = Error;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
let schema = value.serialize(Serializer {
generator: self.generator,
include_title: false,
})?;
self.items.push(schema);
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let len = self.items.len().try_into().ok();
let mut schema = SchemaObject {
instance_type: Some(InstanceType::Array.into()),
array: Some(Box::new(ArrayValidation {
items: Some(SingleOrVec::Vec(self.items)),
max_items: len,
min_items: len,
..ArrayValidation::default()
})),
..SchemaObject::default()
};
if !self.title.is_empty() {
schema.metadata().title = Some(self.title.to_owned());
}
Ok(schema.into())
}
}
impl serde::ser::SerializeTupleStruct for SerializeTuple<'_> {
type Ok = Schema;
type Error = Error;
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
serde::ser::SerializeTuple::serialize_element(self, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
serde::ser::SerializeTuple::end(self)
}
}
impl serde::ser::SerializeMap for SerializeMap<'_> {
type Ok = Schema;
type Error = Error;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
// FIXME this is too lenient - we should return an error if serde_json
// doesn't allow T to be a key of a map.
let json = serde_json::to_string(key)?;
self.current_key = Some(
json.trim_start_matches('"')
.trim_end_matches('"')
.to_string(),
);
Ok(())
}
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
let key = self.current_key.take().unwrap_or_default();
let schema = value.serialize(Serializer {
generator: self.generator,
include_title: false,
})?;
self.properties.insert(key, schema);
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let mut schema = SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(Box::new(ObjectValidation {
properties: self.properties,
..ObjectValidation::default()
})),
..SchemaObject::default()
};
if !self.title.is_empty() {
schema.metadata().title = Some(self.title.to_owned());
}
Ok(schema.into())
}
}
impl serde::ser::SerializeStruct for SerializeMap<'_> {
type Ok = Schema;
type Error = Error;
fn serialize_field<T: ?Sized>(
&mut self,
key: &'static str,
value: &T,
) -> Result<(), Self::Error>
where
T: serde::Serialize,
{
let prop_schema = value.serialize(Serializer {
generator: self.generator,
include_title: false,
})?;
self.properties.insert(key.to_string(), prop_schema);
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
serde::ser::SerializeMap::end(self)
}
}

212
vendor/schemars/src/visit.rs vendored Normal file
View File

@@ -0,0 +1,212 @@
/*!
Contains the [`Visitor`] trait, used to recursively modify a constructed schema and its subschemas.
Sometimes you may want to apply a change to a schema, as well as all schemas contained within it.
The easiest way to achieve this is by defining a type that implements [`Visitor`].
All methods of `Visitor` have a default implementation that makes no change but recursively visits all subschemas.
When overriding one of these methods, you will *usually* want to still call this default implementation.
# Example
To add a custom property to all schemas:
```
use schemars::schema::SchemaObject;
use schemars::visit::{Visitor, visit_schema_object};
pub struct MyVisitor;
impl Visitor for MyVisitor {
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
// First, make our change to this schema
schema.extensions.insert("my_property".to_string(), serde_json::json!("hello world"));
// Then delegate to default implementation to visit any subschemas
visit_schema_object(self, schema);
}
}
```
*/
use crate::schema::{RootSchema, Schema, SchemaObject, SingleOrVec};
/// Trait used to recursively modify a constructed schema and its subschemas.
pub trait Visitor {
/// Override this method to modify a [`RootSchema`] and (optionally) its subschemas.
///
/// When overriding this method, you will usually want to call the [`visit_root_schema`] function to visit subschemas.
fn visit_root_schema(&mut self, root: &mut RootSchema) {
visit_root_schema(self, root)
}
/// Override this method to modify a [`Schema`] and (optionally) its subschemas.
///
/// When overriding this method, you will usually want to call the [`visit_schema`] function to visit subschemas.
fn visit_schema(&mut self, schema: &mut Schema) {
visit_schema(self, schema)
}
/// Override this method to modify a [`SchemaObject`] and (optionally) its subschemas.
///
/// When overriding this method, you will usually want to call the [`visit_schema_object`] function to visit subschemas.
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
visit_schema_object(self, schema)
}
}
/// Visits all subschemas of the [`RootSchema`].
pub fn visit_root_schema<V: Visitor + ?Sized>(v: &mut V, root: &mut RootSchema) {
v.visit_schema_object(&mut root.schema);
visit_map_values(v, &mut root.definitions);
}
/// Visits all subschemas of the [`Schema`].
pub fn visit_schema<V: Visitor + ?Sized>(v: &mut V, schema: &mut Schema) {
if let Schema::Object(schema) = schema {
v.visit_schema_object(schema)
}
}
/// Visits all subschemas of the [`SchemaObject`].
pub fn visit_schema_object<V: Visitor + ?Sized>(v: &mut V, schema: &mut SchemaObject) {
if let Some(sub) = &mut schema.subschemas {
visit_vec(v, &mut sub.all_of);
visit_vec(v, &mut sub.any_of);
visit_vec(v, &mut sub.one_of);
visit_box(v, &mut sub.not);
visit_box(v, &mut sub.if_schema);
visit_box(v, &mut sub.then_schema);
visit_box(v, &mut sub.else_schema);
}
if let Some(arr) = &mut schema.array {
visit_single_or_vec(v, &mut arr.items);
visit_box(v, &mut arr.additional_items);
visit_box(v, &mut arr.contains);
}
if let Some(obj) = &mut schema.object {
visit_map_values(v, &mut obj.properties);
visit_map_values(v, &mut obj.pattern_properties);
visit_box(v, &mut obj.additional_properties);
visit_box(v, &mut obj.property_names);
}
}
fn visit_box<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<Box<Schema>>) {
if let Some(s) = target {
v.visit_schema(s)
}
}
fn visit_vec<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<Vec<Schema>>) {
if let Some(vec) = target {
for s in vec {
v.visit_schema(s)
}
}
}
fn visit_map_values<V: Visitor + ?Sized>(v: &mut V, target: &mut crate::Map<String, Schema>) {
for s in target.values_mut() {
v.visit_schema(s)
}
}
fn visit_single_or_vec<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<SingleOrVec<Schema>>) {
match target {
None => {}
Some(SingleOrVec::Single(s)) => v.visit_schema(s),
Some(SingleOrVec::Vec(vec)) => {
for s in vec {
v.visit_schema(s)
}
}
}
}
/// This visitor will replace all boolean JSON Schemas with equivalent object schemas.
///
/// This is useful for dialects of JSON Schema (e.g. OpenAPI 3.0) that do not support booleans as schemas.
#[derive(Debug, Clone)]
pub struct ReplaceBoolSchemas {
/// When set to `true`, a schema's `additionalProperties` property will not be changed from a boolean.
pub skip_additional_properties: bool,
}
impl Visitor for ReplaceBoolSchemas {
fn visit_schema(&mut self, schema: &mut Schema) {
visit_schema(self, schema);
if let Schema::Bool(b) = *schema {
*schema = Schema::Bool(b).into_object().into()
}
}
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
if self.skip_additional_properties {
if let Some(obj) = &mut schema.object {
if let Some(ap) = &obj.additional_properties {
if let Schema::Bool(_) = ap.as_ref() {
let additional_properties = obj.additional_properties.take();
visit_schema_object(self, schema);
schema.object().additional_properties = additional_properties;
return;
}
}
}
}
visit_schema_object(self, schema);
}
}
/// This visitor will restructure JSON Schema objects so that the `$ref` property will never appear alongside any other properties.
///
/// This is useful for dialects of JSON Schema (e.g. Draft 7) that do not support other properties alongside `$ref`.
#[derive(Debug, Clone)]
pub struct RemoveRefSiblings;
impl Visitor for RemoveRefSiblings {
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
visit_schema_object(self, schema);
if let Some(reference) = schema.reference.take() {
if schema == &SchemaObject::default() {
schema.reference = Some(reference);
} else {
let ref_schema = Schema::new_ref(reference);
let all_of = &mut schema.subschemas().all_of;
match all_of {
Some(vec) => vec.push(ref_schema),
None => *all_of = Some(vec![ref_schema]),
}
}
}
}
}
/// This visitor will remove the `examples` schema property and (if present) set its first value as the `example` property.
///
/// This is useful for dialects of JSON Schema (e.g. OpenAPI 3.0) that do not support the `examples` property.
#[derive(Debug, Clone)]
pub struct SetSingleExample {
/// When set to `true`, the `examples` property will not be removed, but its first value will still be copied to `example`.
pub retain_examples: bool,
}
impl Visitor for SetSingleExample {
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
visit_schema_object(self, schema);
let first_example = schema.metadata.as_mut().and_then(|m| {
if self.retain_examples {
m.examples.first().cloned()
} else {
m.examples.drain(..).next()
}
});
if let Some(example) = first_example {
schema.extensions.insert("example".to_owned(), example);
}
}
}