From fe861ac3a149568585c1dd6ac740f0fa26ed8641 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 27 Oct 2019 22:30:01 +0000 Subject: [PATCH] Implement JsonSchema for Range/RangeInclusive --- schemars/src/json_schema_impls/core.rs | 46 +++++++-- schemars/src/json_schema_impls/primitives.rs | 6 +- schemars/src/json_schema_impls/time.rs | 16 ++-- schemars/src/macros.rs | 2 +- schemars/src/schema.rs | 4 +- .../expected/duration_and_systemtime.json | 8 ++ schemars/tests/expected/range.json | 53 +++++++++++ schemars/tests/expected/result.json | 95 +++++++++++++------ schemars/tests/range.rs | 15 +++ schemars/tests/result.rs | 8 +- 10 files changed, 203 insertions(+), 50 deletions(-) create mode 100644 schemars/tests/expected/range.json create mode 100644 schemars/tests/range.rs diff --git a/schemars/src/json_schema_impls/core.rs b/schemars/src/json_schema_impls/core.rs index a0426e7..23344fd 100644 --- a/schemars/src/json_schema_impls/core.rs +++ b/schemars/src/json_schema_impls/core.rs @@ -2,6 +2,7 @@ use crate::gen::SchemaGenerator; use crate::schema::*; use crate::JsonSchema; use serde_json::json; +use std::ops::{Range, RangeInclusive}; impl JsonSchema for Option { no_ref_schema!(); @@ -74,8 +75,6 @@ fn with_null_type(mut obj: SchemaObject) -> SchemaObject { } impl JsonSchema for Result { - no_ref_schema!(); - fn schema_name() -> String { format!("Result_Of_{}_Or_{}", T::schema_name(), E::schema_name()) } @@ -83,18 +82,16 @@ impl JsonSchema for Result { fn json_schema(gen: &mut SchemaGenerator) -> Schema { let mut ok_schema = SchemaObject::default(); ok_schema.instance_type = Some(InstanceType::Object.into()); - ok_schema.object().required.insert("Ok".to_owned()); - ok_schema - .object() - .properties + let obj = ok_schema.object(); + obj.required.insert("Ok".to_owned()); + obj.properties .insert("Ok".to_owned(), gen.subschema_for::()); let mut err_schema = SchemaObject::default(); err_schema.instance_type = Some(InstanceType::Object.into()); - err_schema.object().required.insert("Err".to_owned()); - err_schema - .object() - .properties + let obj = err_schema.object(); + obj.required.insert("Err".to_owned()); + obj.properties .insert("Err".to_owned(), gen.subschema_for::()); let mut schema = SchemaObject::default(); @@ -103,6 +100,35 @@ impl JsonSchema for Result { } } +impl JsonSchema for Range { + 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::()); + obj.properties + .insert("end".to_owned(), gen.subschema_for::()); + schema.into() + } +} + +impl JsonSchema for RangeInclusive { + fn schema_name() -> String { + >::schema_name() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + >::json_schema(gen) + } +} + impl JsonSchema for std::marker::PhantomData { no_ref_schema!(); diff --git a/schemars/src/json_schema_impls/primitives.rs b/schemars/src/json_schema_impls/primitives.rs index fc2665b..6a25ede 100644 --- a/schemars/src/json_schema_impls/primitives.rs +++ b/schemars/src/json_schema_impls/primitives.rs @@ -5,13 +5,13 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV use std::path::{Path, PathBuf}; macro_rules! simple_impl { - ($type:tt => $instance_type:ident) => { + ($type:ty => $instance_type:ident) => { 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())); }; - ($type:tt => $instance_type:ident, $($format:tt)+) => { + ($type:ty => $instance_type:ident, $($format:tt)+) => { impl JsonSchema for $type { no_ref_schema!(); diff --git a/schemars/src/json_schema_impls/time.rs b/schemars/src/json_schema_impls/time.rs index 38d35b2..55d1558 100644 --- a/schemars/src/json_schema_impls/time.rs +++ b/schemars/src/json_schema_impls/time.rs @@ -11,9 +11,11 @@ impl JsonSchema for Duration { fn json_schema(gen: &mut SchemaGenerator) -> Schema { let mut schema = SchemaObject::default(); schema.instance_type = Some(InstanceType::Object.into()); - let properties = &mut schema.object().properties; - properties.insert("secs".to_owned(), ::json_schema(gen)); - properties.insert("nanos".to_owned(), ::json_schema(gen)); + let obj = schema.object(); + obj.required.insert("secs".to_owned()); + obj.required.insert("nanos".to_owned()); + obj.properties.insert("secs".to_owned(), ::json_schema(gen)); + obj.properties.insert("nanos".to_owned(), ::json_schema(gen)); schema.into() } } @@ -26,9 +28,11 @@ impl JsonSchema for SystemTime { fn json_schema(gen: &mut SchemaGenerator) -> Schema { let mut schema = SchemaObject::default(); schema.instance_type = Some(InstanceType::Object.into()); - let properties = &mut schema.object().properties; - properties.insert("secs_since_epoch".to_owned(), ::json_schema(gen)); - properties.insert("nanos_since_epoch".to_owned(), ::json_schema(gen)); + 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(), ::json_schema(gen)); + obj.properties.insert("nanos_since_epoch".to_owned(), ::json_schema(gen)); schema.into() } } diff --git a/schemars/src/macros.rs b/schemars/src/macros.rs index aac88ff..2268826 100644 --- a/schemars/src/macros.rs +++ b/schemars/src/macros.rs @@ -15,7 +15,7 @@ /// ``` #[macro_export] macro_rules! schema_for { - ($type:path) => { + ($type:ty) => { $crate::gen::SchemaGenerator::default().into_root_schema_for::<$type>() }; } diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index f1c2ed0..b0f79f7 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -144,14 +144,14 @@ where } macro_rules! get_or_insert_default_fn { - ($name:ident, $ret:path) => { + ($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:path) => { + ($doc:expr, $name:ident, $ret:ty) => { #[doc = $doc] pub fn $name(&mut self) -> &mut $ret { self.$name.get_or_insert_with(Default::default) diff --git a/schemars/tests/expected/duration_and_systemtime.json b/schemars/tests/expected/duration_and_systemtime.json index e52d28f..cb91eb4 100644 --- a/schemars/tests/expected/duration_and_systemtime.json +++ b/schemars/tests/expected/duration_and_systemtime.json @@ -17,6 +17,10 @@ "definitions": { "Duration": { "type": "object", + "required": [ + "nanos", + "secs" + ], "properties": { "nanos": { "type": "integer", @@ -30,6 +34,10 @@ }, "SystemTime": { "type": "object", + "required": [ + "nanos_since_epoch", + "secs_since_epoch" + ], "properties": { "nanos_since_epoch": { "type": "integer", diff --git a/schemars/tests/expected/range.json b/schemars/tests/expected/range.json new file mode 100644 index 0000000..687e20d --- /dev/null +++ b/schemars/tests/expected/range.json @@ -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" + } + } + } + } +} \ No newline at end of file diff --git a/schemars/tests/expected/result.json b/schemars/tests/expected/result.json index 42d4642..0c3434b 100644 --- a/schemars/tests/expected/result.json +++ b/schemars/tests/expected/result.json @@ -1,33 +1,19 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "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" - } - } - } - } + "title": "Container", + "type": "object", + "required": [ + "result1", + "result2" ], + "properties": { + "result1": { + "$ref": "#/definitions/Result_Of_MyStruct_Or_Array_Of_String" + }, + "result2": { + "$ref": "#/definitions/Result_Of_Boolean_Or_Null" + } + }, "definitions": { "MyStruct": { "type": "object", @@ -40,6 +26,61 @@ "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" + } + } + } + } + ] } } } \ No newline at end of file diff --git a/schemars/tests/range.rs b/schemars/tests/range.rs new file mode 100644 index 0000000..19b5ad2 --- /dev/null +++ b/schemars/tests/range.rs @@ -0,0 +1,15 @@ +mod util; +use schemars::JsonSchema; +use util::*; +use std::ops::{Range, RangeInclusive}; + +#[derive(Debug, JsonSchema)] +struct MyStruct { + range: Range, + inclusive: RangeInclusive, +} + +#[test] +fn result() -> TestResult { + test_default_generated_schema::("range") +} diff --git a/schemars/tests/result.rs b/schemars/tests/result.rs index e1030cf..0da7e53 100644 --- a/schemars/tests/result.rs +++ b/schemars/tests/result.rs @@ -7,7 +7,13 @@ struct MyStruct { foo: i32, } +#[derive(Debug, JsonSchema)] +struct Container { + result1: Result>, + result2: Result, +} + #[test] fn result() -> TestResult { - test_default_generated_schema::>>("result") + test_default_generated_schema::("result") }