use crate::gen::SchemaGenerator;
use crate::schema::*;
use crate::JsonSchema;
use serde_json::json;
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 json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut schema = gen.subschema_for::<T>();
if gen.settings().option_add_null_type {
schema = match schema {
Schema::Bool(true) => Schema::Bool(true),
Schema::Bool(false) => <()>::json_schema(gen),
Schema::Object(
obj @ SchemaObject {
instance_type: Some(_),
..
},
) => Schema::Object(with_null_type(obj)),
schema => SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(vec![schema, <()>::json_schema(gen)]),
..Default::default()
})),
..Default::default()
}
.into(),
}
}
if gen.settings().option_nullable {
let mut schema_obj = gen.make_extensible(schema.into());
schema_obj
.extensions
.insert("nullable".to_owned(), json!(true));
schema = Schema::Object(schema_obj);
};
schema
}
fn json_schema_optional(gen: &mut SchemaGenerator) -> Schema {
let mut schema = T::json_schema_optional(gen);
if let Schema::Object(SchemaObject {
object: Some(ref mut object_validation),
..
}) = schema
{
object_validation.required.clear();
}
schema
}
}
fn with_null_type(mut obj: SchemaObject) -> SchemaObject {
match obj
.instance_type
.as_mut()
.expect("checked in calling function")
{
SingleOrVec::Single(ty) if **ty == InstanceType::Null => {}
SingleOrVec::Vec(ty) if ty.contains(&InstanceType::Null) => {}
SingleOrVec::Single(ty) => obj.instance_type = Some(vec![**ty, InstanceType::Null].into()),
SingleOrVec::Vec(ref mut ty) => ty.push(InstanceType::Null),
};
obj
}
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 json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut ok_schema = SchemaObject::default();
ok_schema.instance_type = Some(InstanceType::Object.into());
let obj = ok_schema.object();
obj.required.insert("Ok".to_owned());
obj.properties
.insert("Ok".to_owned(), gen.subschema_for::<T>());
let mut err_schema = SchemaObject::default();
err_schema.instance_type = Some(InstanceType::Object.into());
let obj = err_schema.object();
obj.required.insert("Err".to_owned());
obj.properties
.insert("Err".to_owned(), gen.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 json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut included_schema = SchemaObject::default();
included_schema.instance_type = Some(InstanceType::Object.into());
included_schema
.object()
.required
.insert("Included".to_owned());
included_schema
.object()
.properties
.insert("Included".to_owned(), gen.subschema_for::<T>());
let mut excluded_schema = SchemaObject::default();
excluded_schema.instance_type = Some(InstanceType::Object.into());
excluded_schema
.object()
.required
.insert("Excluded".to_owned());
excluded_schema
.object()
.properties
.insert("Excluded".to_owned(), gen.subschema_for::<T>());
let mut unbounded_schema = SchemaObject::default();
unbounded_schema.instance_type = Some(InstanceType::String.into());
unbounded_schema.const_value = Some(json!("Unbounded"));
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 json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject::default();
schema.instance_type = Some(InstanceType::Object.into());
let obj = schema.object();
obj.required.insert("start".to_owned());
obj.required.insert("end".to_owned());
obj.properties
.insert("start".to_owned(), gen.subschema_for::<T>());
obj.properties
.insert("end".to_owned(), gen.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::gen::*;
use crate::tests::{custom_schema_object_for, 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_option_with_nullable() {
let settings = SchemaSettings {
option_nullable: true,
option_add_null_type: false,
..Default::default()
};
let schema = custom_schema_object_for::<Option<i32>>(settings);
assert_eq!(
schema.instance_type,
Some(SingleOrVec::from(InstanceType::Integer))
);
assert_eq!(schema.extensions.get("nullable"), Some(&json!(true)));
assert_eq!(schema.subschemas.is_none(), true);
}
#[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>());
}
}