Implement JsonSchema for Range/RangeInclusive

This commit is contained in:
Graham Esau 2019-10-27 22:30:01 +00:00
parent b334bef91a
commit fe861ac3a1
10 changed files with 203 additions and 50 deletions

View file

@ -2,6 +2,7 @@ use crate::gen::SchemaGenerator;
use crate::schema::*; use crate::schema::*;
use crate::JsonSchema; use crate::JsonSchema;
use serde_json::json; use serde_json::json;
use std::ops::{Range, RangeInclusive};
impl<T: JsonSchema> JsonSchema for Option<T> { impl<T: JsonSchema> JsonSchema for Option<T> {
no_ref_schema!(); no_ref_schema!();
@ -74,8 +75,6 @@ fn with_null_type(mut obj: SchemaObject) -> SchemaObject {
} }
impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> { impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
no_ref_schema!();
fn schema_name() -> String { fn schema_name() -> String {
format!("Result_Of_{}_Or_{}", T::schema_name(), E::schema_name()) format!("Result_Of_{}_Or_{}", T::schema_name(), E::schema_name())
} }
@ -83,18 +82,16 @@ impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
fn json_schema(gen: &mut SchemaGenerator) -> Schema { fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut ok_schema = SchemaObject::default(); let mut ok_schema = SchemaObject::default();
ok_schema.instance_type = Some(InstanceType::Object.into()); ok_schema.instance_type = Some(InstanceType::Object.into());
ok_schema.object().required.insert("Ok".to_owned()); let obj = ok_schema.object();
ok_schema obj.required.insert("Ok".to_owned());
.object() obj.properties
.properties
.insert("Ok".to_owned(), gen.subschema_for::<T>()); .insert("Ok".to_owned(), gen.subschema_for::<T>());
let mut err_schema = SchemaObject::default(); let mut err_schema = SchemaObject::default();
err_schema.instance_type = Some(InstanceType::Object.into()); err_schema.instance_type = Some(InstanceType::Object.into());
err_schema.object().required.insert("Err".to_owned()); let obj = err_schema.object();
err_schema obj.required.insert("Err".to_owned());
.object() obj.properties
.properties
.insert("Err".to_owned(), gen.subschema_for::<E>()); .insert("Err".to_owned(), gen.subschema_for::<E>());
let mut schema = SchemaObject::default(); let mut schema = SchemaObject::default();
@ -103,6 +100,35 @@ impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
} }
} }
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()
}
}
impl<T: JsonSchema> JsonSchema for RangeInclusive<T> {
fn schema_name() -> String {
<Range<T>>::schema_name()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
<Range<T>>::json_schema(gen)
}
}
impl<T: ?Sized> JsonSchema for std::marker::PhantomData<T> { impl<T: ?Sized> JsonSchema for std::marker::PhantomData<T> {
no_ref_schema!(); no_ref_schema!();

View file

@ -5,13 +5,13 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
macro_rules! simple_impl { macro_rules! simple_impl {
($type:tt => $instance_type:ident) => { ($type:ty => $instance_type:ident) => {
simple_impl!($type => $instance_type, None); simple_impl!($type => $instance_type, None);
}; };
($type:tt => $instance_type:ident, $format: literal) => { ($type:ty => $instance_type:ident, $format: literal) => {
simple_impl!($type => $instance_type, Some($format.to_owned())); simple_impl!($type => $instance_type, Some($format.to_owned()));
}; };
($type:tt => $instance_type:ident, $($format:tt)+) => { ($type:ty => $instance_type:ident, $($format:tt)+) => {
impl JsonSchema for $type { impl JsonSchema for $type {
no_ref_schema!(); no_ref_schema!();

View file

@ -11,9 +11,11 @@ impl JsonSchema for Duration {
fn json_schema(gen: &mut SchemaGenerator) -> Schema { fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject::default(); let mut schema = SchemaObject::default();
schema.instance_type = Some(InstanceType::Object.into()); schema.instance_type = Some(InstanceType::Object.into());
let properties = &mut schema.object().properties; let obj = schema.object();
properties.insert("secs".to_owned(), <u64>::json_schema(gen)); obj.required.insert("secs".to_owned());
properties.insert("nanos".to_owned(), <u32>::json_schema(gen)); obj.required.insert("nanos".to_owned());
obj.properties.insert("secs".to_owned(), <u64>::json_schema(gen));
obj.properties.insert("nanos".to_owned(), <u32>::json_schema(gen));
schema.into() schema.into()
} }
} }
@ -26,9 +28,11 @@ impl JsonSchema for SystemTime {
fn json_schema(gen: &mut SchemaGenerator) -> Schema { fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut schema = SchemaObject::default(); let mut schema = SchemaObject::default();
schema.instance_type = Some(InstanceType::Object.into()); schema.instance_type = Some(InstanceType::Object.into());
let properties = &mut schema.object().properties; let obj = schema.object();
properties.insert("secs_since_epoch".to_owned(), <u64>::json_schema(gen)); obj.required.insert("secs_since_epoch".to_owned());
properties.insert("nanos_since_epoch".to_owned(), <u32>::json_schema(gen)); obj.required.insert("nanos_since_epoch".to_owned());
obj.properties.insert("secs_since_epoch".to_owned(), <u64>::json_schema(gen));
obj.properties.insert("nanos_since_epoch".to_owned(), <u32>::json_schema(gen));
schema.into() schema.into()
} }
} }

View file

@ -15,7 +15,7 @@
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! schema_for { macro_rules! schema_for {
($type:path) => { ($type:ty) => {
$crate::gen::SchemaGenerator::default().into_root_schema_for::<$type>() $crate::gen::SchemaGenerator::default().into_root_schema_for::<$type>()
}; };
} }

View file

@ -144,14 +144,14 @@ where
} }
macro_rules! get_or_insert_default_fn { macro_rules! get_or_insert_default_fn {
($name:ident, $ret:path) => { ($name:ident, $ret:ty) => {
get_or_insert_default_fn!( 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`."), concat!("Returns a mutable reference to this schema's [`", stringify!($ret), "`](#structfield.", stringify!($name), "), creating it if it was `None`."),
$name, $name,
$ret $ret
); );
}; };
($doc:expr, $name:ident, $ret:path) => { ($doc:expr, $name:ident, $ret:ty) => {
#[doc = $doc] #[doc = $doc]
pub fn $name(&mut self) -> &mut $ret { pub fn $name(&mut self) -> &mut $ret {
self.$name.get_or_insert_with(Default::default) self.$name.get_or_insert_with(Default::default)

View file

@ -17,6 +17,10 @@
"definitions": { "definitions": {
"Duration": { "Duration": {
"type": "object", "type": "object",
"required": [
"nanos",
"secs"
],
"properties": { "properties": {
"nanos": { "nanos": {
"type": "integer", "type": "integer",
@ -30,6 +34,10 @@
}, },
"SystemTime": { "SystemTime": {
"type": "object", "type": "object",
"required": [
"nanos_since_epoch",
"secs_since_epoch"
],
"properties": { "properties": {
"nanos_since_epoch": { "nanos_since_epoch": {
"type": "integer", "type": "integer",

View file

@ -0,0 +1,53 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MyStruct",
"type": "object",
"required": [
"inclusive",
"range"
],
"properties": {
"inclusive": {
"$ref": "#/definitions/Range_Of_Number"
},
"range": {
"$ref": "#/definitions/Range_Of_Integer"
}
},
"definitions": {
"Range_Of_Integer": {
"type": "object",
"required": [
"end",
"start"
],
"properties": {
"end": {
"type": "integer",
"format": "uint"
},
"start": {
"type": "integer",
"format": "uint"
}
}
},
"Range_Of_Number": {
"type": "object",
"required": [
"end",
"start"
],
"properties": {
"end": {
"type": "number",
"format": "double"
},
"start": {
"type": "number",
"format": "double"
}
}
}
}
}

View file

@ -1,33 +1,19 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Result_Of_MyStruct_Or_Array_Of_String", "title": "Container",
"oneOf": [ "type": "object",
{ "required": [
"type": "object", "result1",
"required": [ "result2"
"Ok"
],
"properties": {
"Ok": {
"$ref": "#/definitions/MyStruct"
}
}
},
{
"type": "object",
"required": [
"Err"
],
"properties": {
"Err": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
], ],
"properties": {
"result1": {
"$ref": "#/definitions/Result_Of_MyStruct_Or_Array_Of_String"
},
"result2": {
"$ref": "#/definitions/Result_Of_Boolean_Or_Null"
}
},
"definitions": { "definitions": {
"MyStruct": { "MyStruct": {
"type": "object", "type": "object",
@ -40,6 +26,61 @@
"format": "int32" "format": "int32"
} }
} }
},
"Result_Of_Boolean_Or_Null": {
"oneOf": [
{
"type": "object",
"required": [
"Ok"
],
"properties": {
"Ok": {
"type": "boolean"
}
}
},
{
"type": "object",
"required": [
"Err"
],
"properties": {
"Err": {
"type": "null"
}
}
}
]
},
"Result_Of_MyStruct_Or_Array_Of_String": {
"oneOf": [
{
"type": "object",
"required": [
"Ok"
],
"properties": {
"Ok": {
"$ref": "#/definitions/MyStruct"
}
}
},
{
"type": "object",
"required": [
"Err"
],
"properties": {
"Err": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
]
} }
} }
} }

15
schemars/tests/range.rs Normal file
View file

@ -0,0 +1,15 @@
mod util;
use schemars::JsonSchema;
use util::*;
use std::ops::{Range, RangeInclusive};
#[derive(Debug, JsonSchema)]
struct MyStruct {
range: Range<usize>,
inclusive: RangeInclusive<f64>,
}
#[test]
fn result() -> TestResult {
test_default_generated_schema::<MyStruct>("range")
}

View file

@ -7,7 +7,13 @@ struct MyStruct {
foo: i32, foo: i32,
} }
#[derive(Debug, JsonSchema)]
struct Container {
result1: Result<MyStruct, Vec<String>>,
result2: Result<bool, ()>,
}
#[test] #[test]
fn result() -> TestResult { fn result() -> TestResult {
test_default_generated_schema::<Result<MyStruct, Vec<String>>>("result") test_default_generated_schema::<Container>("result")
} }