Merge branch 'master' into validate

This commit is contained in:
Graham Esau 2021-04-15 16:03:25 +01:00
commit b68132f17d
31 changed files with 440 additions and 143 deletions

View file

@ -3,7 +3,7 @@ name = "schemars"
description = "Generate JSON Schemas from Rust code"
homepage = "https://graham.cool/schemars/"
repository = "https://github.com/GREsau/schemars"
version = "0.8.2"
version = "0.8.3"
authors = ["Graham Esau <gesau@hotmail.co.uk>"]
edition = "2018"
license = "MIT"
@ -13,7 +13,7 @@ categories = ["encoding"]
build = "build.rs"
[dependencies]
schemars_derive = { version = "=0.8.2", optional = true, path = "../schemars_derive" }
schemars_derive = { version = "=0.8.3", optional = true, path = "../schemars_derive" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
dyn-clone = "1.0"
@ -25,6 +25,7 @@ uuid = { version = "0.8", default-features = false, optional = true }
smallvec = { version = "1.0", optional = true }
arrayvec = { version = "0.5", default-features = false, optional = true }
url = { version = "2.0", default-features = false, optional = true }
bytes = { version = "1.0", optional = true }
[dev-dependencies]
pretty_assertions = "0.6.1"
@ -66,6 +67,10 @@ required-features = ["uuid"]
name = "smallvec"
required-features = ["smallvec"]
[[test]]
name = "bytes"
required-features = ["bytes"]
[[test]]
name = "arrayvec"
required-features = ["arrayvec"]

55
schemars/src/_private.rs Normal file
View file

@ -0,0 +1,55 @@
use crate::flatten::Merge;
use crate::gen::SchemaGenerator;
use crate::schema::{Metadata, Schema, SchemaObject};
use crate::JsonSchema;
// Helper for generating schemas for flattened `Option` fields.
pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(gen: &mut SchemaGenerator) -> Schema {
let mut schema = T::_schemars_private_non_optional_json_schema(gen);
if T::_schemars_private_is_option() {
if let Schema::Object(SchemaObject {
object: Some(ref mut object_validation),
..
}) = schema
{
object_validation.required.clear();
}
}
schema
}
// Helper for generating schemas for `Option` fields.
pub fn add_schema_as_property<T: ?Sized + JsonSchema>(
gen: &mut SchemaGenerator,
parent: &mut SchemaObject,
name: String,
metadata: Option<Metadata>,
required: Option<bool>,
) {
let is_type_option = T::_schemars_private_is_option();
let required = required.unwrap_or(!is_type_option);
let mut schema = if required && is_type_option {
T::_schemars_private_non_optional_json_schema(gen)
} else {
gen.subschema_for::<T>()
};
schema = apply_metadata(schema, metadata);
let object = parent.object();
if required {
object.required.insert(name.clone());
}
object.properties.insert(name, schema);
}
pub fn apply_metadata(schema: Schema, metadata: Option<Metadata>) -> Schema {
match metadata {
None => schema,
Some(ref metadata) if *metadata == Metadata::default() => schema,
Some(metadata) => {
let mut schema_obj = schema.into_object();
schema_obj.metadata = Some(Box::new(metadata)).merge(schema_obj.metadata);
Schema::Object(schema_obj)
}
}
}

View file

@ -7,7 +7,6 @@ There are two main types in this module:two main types in this module:
* [`SchemaGenerator`], which manages the generation of a schema document.
*/
use crate::flatten::Merge;
use crate::schema::*;
use crate::{visit::*, JsonSchema, Map};
use dyn_clone::DynClone;
@ -422,22 +421,6 @@ impl SchemaGenerator {
}
}
/// This function is only public for use by schemars_derive.
///
/// It should not be considered part of the public API.
#[doc(hidden)]
pub fn apply_metadata(&self, schema: Schema, metadata: Option<Metadata>) -> Schema {
match metadata {
None => schema,
Some(ref metadata) if *metadata == Metadata::default() => schema,
Some(metadata) => {
let mut schema_obj = schema.into_object();
schema_obj.metadata = Some(Box::new(metadata)).merge(schema_obj.metadata);
Schema::Object(schema_obj)
}
}
}
fn json_schema_internal<T: ?Sized + JsonSchema>(&mut self, name: &str) -> Schema {
struct PendingSchemaState<'a> {
gen: &'a mut SchemaGenerator,

View file

@ -0,0 +1,7 @@
use crate::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

@ -45,34 +45,12 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
schema
}
fn json_schema_for_flatten(gen: &mut SchemaGenerator) -> Schema {
let mut schema = T::json_schema_for_flatten(gen);
if let Schema::Object(SchemaObject {
object: Some(ref mut object_validation),
..
}) = schema
{
object_validation.required.clear();
}
schema
fn _schemars_private_non_optional_json_schema(gen: &mut SchemaGenerator) -> Schema {
T::_schemars_private_non_optional_json_schema(gen)
}
fn add_schema_as_property(
gen: &mut SchemaGenerator,
parent: &mut SchemaObject,
name: String,
metadata: Option<Metadata>,
required: Option<bool>,
) {
if required == Some(true) {
T::add_schema_as_property(gen, parent, name, metadata, required)
} else {
let mut schema = gen.subschema_for::<Self>();
schema = gen.apply_metadata(schema, metadata);
let object = parent.object();
object.properties.insert(name, schema);
}
fn _schemars_private_is_option() -> bool {
true
}
}

View file

@ -21,18 +21,12 @@ macro_rules! forward_impl {
<$target>::json_schema(gen)
}
fn json_schema_for_flatten(gen: &mut SchemaGenerator) -> Schema {
<$target>::json_schema_for_flatten(gen)
fn _schemars_private_non_optional_json_schema(gen: &mut SchemaGenerator) -> Schema {
<$target>::_schemars_private_non_optional_json_schema(gen)
}
fn add_schema_as_property(
gen: &mut SchemaGenerator,
parent: &mut crate::schema::SchemaObject,
name: String,
metadata: Option<crate::schema::Metadata>,
required: Option<bool>,
) {
<$target>::add_schema_as_property(gen, parent, name, metadata, required)
fn _schemars_private_is_option() -> bool {
<$target>::_schemars_private_is_option()
}
}
};
@ -46,6 +40,8 @@ mod array;
mod arrayvec;
#[cfg(std_atomic)]
mod atomic;
#[cfg(feature = "bytes")]
mod bytes;
#[cfg(feature = "chrono")]
mod chrono;
mod core;

View file

@ -9,7 +9,9 @@ macro_rules! tuple_impls {
no_ref_schema!();
fn schema_name() -> String {
["Tuple_of".to_owned()$(, $name::schema_name())+].join("_and_")
let mut name = "Tuple_of_".to_owned();
name.push_str(&[$($name::schema_name()),+].join("_and_"));
name
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {

View file

@ -268,6 +268,7 @@ Schemars can implement `JsonSchema` on types from several popular crates, enable
- [`smallvec`](https://crates.io/crates/smallvec) (^1.0)
- [`arrayvec`](https://crates.io/crates/arrayvec) (^0.5)
- [`url`](https://crates.io/crates/url) (^2.0)
- [`bytes`](https://crates.io/crates/bytes) (^1.0)
*/
/// The map type used by schemars types.
@ -300,6 +301,10 @@ 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 gen;
pub mod schema;
pub mod visit;
@ -313,7 +318,7 @@ pub use schemars_derive::*;
#[doc(hidden)]
pub use serde_json as _serde_json;
use schema::{Schema, SchemaObject};
use schema::Schema;
/// A type which can be described as a JSON Schema document.
///
@ -356,35 +361,16 @@ pub trait JsonSchema {
/// This should not return a `$ref` schema.
fn json_schema(gen: &mut gen::SchemaGenerator) -> Schema;
/// Helper for generating schemas for flattened `Option` fields.
///
/// This should not need to be called or implemented by code outside of `schemars`,
/// and should not be considered part of the public API.
// TODO document and bring into public API?
#[doc(hidden)]
fn json_schema_for_flatten(gen: &mut gen::SchemaGenerator) -> Schema {
fn _schemars_private_non_optional_json_schema(gen: &mut gen::SchemaGenerator) -> Schema {
Self::json_schema(gen)
}
/// Helper for generating schemas for `Option` fields.
///
/// This should not need to be called or implemented by code outside of `schemars`,
/// and should not be considered part of the public API.
// TODO document and bring into public API?
#[doc(hidden)]
fn add_schema_as_property(
gen: &mut gen::SchemaGenerator,
parent: &mut SchemaObject,
name: String,
metadata: Option<schema::Metadata>,
required: Option<bool>,
) {
let mut schema = gen.subschema_for::<Self>();
schema = gen.apply_metadata(schema, metadata);
let object = parent.object();
if required.unwrap_or(true) {
object.required.insert(name.clone());
}
object.properties.insert(name, schema);
fn _schemars_private_is_option() -> bool {
false
}
}

8
schemars/tests/bytes.rs Normal file
View file

@ -0,0 +1,8 @@
mod util;
use bytes::{Bytes, BytesMut};
use util::*;
#[test]
fn bytes() -> TestResult {
test_default_generated_schema::<(Bytes, BytesMut)>("bytes")
}

View file

@ -0,0 +1,19 @@
mod util;
use ::schemars as not_schemars;
use util::*;
#[allow(unused_imports)]
use std as schemars;
#[derive(Debug, not_schemars::JsonSchema)]
#[schemars(crate = "not_schemars")]
pub struct Struct {
/// This is a document
foo: i32,
bar: bool,
}
#[test]
fn test_crate_alias() -> TestResult {
test_default_generated_schema::<Struct>("crate_alias")
}

View file

@ -14,8 +14,8 @@ pub struct Struct {
bar: bool,
}
// Outer container should always have additionalPropreties: false
// `Struct` variant should have additionalPropreties: false
// Outer container should always have additionalProperties: false
// `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)]
#[schemars(rename_all = "camelCase", deny_unknown_fields)]
pub enum External {
@ -38,7 +38,7 @@ fn enum_external_tag() -> TestResult {
test_default_generated_schema::<External>("enum-external-duf")
}
// Only `Struct` variant should have additionalPropreties: false
// Only `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)]
#[schemars(tag = "typeProperty", deny_unknown_fields)]
pub enum Internal {
@ -60,7 +60,7 @@ fn enum_internal_tag() -> TestResult {
test_default_generated_schema::<Internal>("enum-internal-duf")
}
// Only `Struct` variant should have additionalPropreties: false
// Only `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)]
#[schemars(untagged, deny_unknown_fields)]
pub enum Untagged {
@ -82,7 +82,7 @@ fn enum_untagged() -> TestResult {
test_default_generated_schema::<Untagged>("enum-untagged-duf")
}
// Outer container and `Struct` variant should have additionalPropreties: false
// Outer container and `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)]
#[schemars(tag = "t", content = "c", deny_unknown_fields)]
pub enum Adjacent {

View file

@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Tuple_of_Array_of_uint8_and_Array_of_uint8",
"type": "array",
"items": [
{
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
},
{
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
}
],
"maxItems": 2,
"minItems": 2
}

View file

@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Struct",
"type": "object",
"required": [
"bar",
"foo"
],
"properties": {
"foo": {
"description": "This is a document",
"type": "integer",
"format": "int32"
},
"bar": {
"type": "boolean"
}
}
}

View file

@ -0,0 +1,23 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "OuterEnum",
"anyOf": [
{
"type": "object",
"required": [
"InnerStruct"
],
"properties": {
"InnerStruct": {
"$ref": "#/definitions/InnerStruct"
}
},
"additionalProperties": false
}
],
"definitions": {
"InnerStruct": {
"type": "object"
}
}
}

View file

@ -0,0 +1,20 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "A",
"type": "object",
"required": [
"v",
"x"
],
"properties": {
"x": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
"v": {
"type": "integer",
"format": "int32"
}
}
}

View file

@ -74,6 +74,24 @@
"minItems": 2
}
}
},
{
"type": "object",
"required": [
"c",
"t"
],
"properties": {
"t": {
"type": "string",
"enum": [
"Unit"
]
},
"c": {
"type": "boolean"
}
}
}
]
}

View file

@ -56,6 +56,18 @@
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"unit"
],
"properties": {
"unit": {
"type": "boolean"
}
},
"additionalProperties": false
}
]
}

View file

@ -36,6 +36,23 @@
]
}
}
},
{
"type": [
"boolean",
"object"
],
"required": [
"typeProperty"
],
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"Unit"
]
}
}
}
]
}

View file

@ -29,6 +29,9 @@
],
"maxItems": 2,
"minItems": 2
},
{
"type": "boolean"
}
]
}

67
schemars/tests/macro.rs Normal file
View file

@ -0,0 +1,67 @@
mod util;
use schemars::JsonSchema;
use util::*;
macro_rules! build_struct {
(
$id:ident { $($t:tt)* }
) => {
#[derive(Debug, JsonSchema)]
pub struct $id {
x: u8,
$($t)*
}
};
}
build_struct!(A { v: i32 });
#[test]
fn macro_built_struct() -> TestResult {
test_default_generated_schema::<A>("macro_built_struct")
}
macro_rules! build_enum {
(
$(#[$outer_derive:meta])*
$outer:ident {
$($(#[$inner_derive:meta])*
$inner:ident {
$( $(#[$field_attribute:meta])*
$field:ident : $ty:ty),*
})*
}
) => {
$(
$(#[$inner_derive])*
pub struct $inner {
$(
$(#[$field_attribute])*
pub $field: $ty
),*
}
)*
$(#[$outer_derive])*
pub enum $outer {
$(
$inner($inner)
),*
}
}
}
build_enum!(
#[derive(Debug, JsonSchema)]
OuterEnum {
#[derive(Debug, JsonSchema)]
InnerStruct {}
}
);
#[test]
fn macro_built_enum() -> TestResult {
test_default_generated_schema::<OuterEnum>("macro_built_enum")
}

View file

@ -2,8 +2,6 @@ mod util;
use schemars::JsonSchema;
use util::*;
// FIXME determine whether schema_with should be allowed on unit variants
fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<bool>::json_schema(gen)
}
@ -23,8 +21,8 @@ pub enum External {
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
i32,
),
// #[schemars(schema_with = "schema_fn")]
// Unit,
#[schemars(schema_with = "schema_fn")]
Unit,
}
#[test]
@ -40,8 +38,8 @@ pub enum Internal {
foo: DoesntImplementJsonSchema,
},
NewType(#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema),
// #[schemars(schema_with = "schema_fn")]
// Unit,
#[schemars(schema_with = "schema_fn")]
Unit,
}
#[test]
@ -61,8 +59,8 @@ pub enum Untagged {
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
i32,
),
// #[schemars(schema_with = "schema_fn")]
// Unit,
#[schemars(schema_with = "schema_fn")]
Unit,
}
#[test]
@ -82,8 +80,8 @@ pub enum Adjacent {
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
i32,
),
// #[schemars(schema_with = "schema_fn")]
// Unit,
#[schemars(schema_with = "schema_fn")]
Unit,
}
#[test]