Generate schema from any serializable value (#75)

Implement schema_for_value!(...) macro
This commit is contained in:
Graham Esau 2021-03-25 18:32:28 +00:00 committed by GitHub
parent 0957204bc1
commit f6482fd460
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1179 additions and 5 deletions

View file

@ -0,0 +1,80 @@
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"title": "MyStruct",
"examples": [
{
"myBool": true,
"myInnerStruct": {
"my_empty_map": {},
"my_empty_vec": [],
"my_map": {
"": 0.0
},
"my_tuple": [
"💩",
42
],
"my_vec": [
"hello",
"world"
]
},
"myInt": 123,
"myNullableEnum": null
}
],
"type": "object",
"properties": {
"myInt": {
"type": "integer",
"format": "int32"
},
"myBool": {
"type": "boolean"
},
"myNullableEnum": true,
"myInnerStruct": {
"type": "object",
"properties": {
"my_map": {
"type": "object",
"additionalProperties": {
"type": "number",
"format": "double"
}
},
"my_vec": {
"type": "array",
"items": {
"type": "string"
}
},
"my_empty_map": {
"type": "object",
"additionalProperties": true
},
"my_empty_vec": {
"type": "array",
"items": true
},
"my_tuple": {
"type": "array",
"items": [
{
"type": "string",
"maxLength": 1,
"minLength": 1
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
],
"maxItems": 2,
"minItems": 2
}
}
}
}
}

View file

@ -0,0 +1,80 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MyStruct",
"examples": [
{
"myBool": true,
"myInnerStruct": {
"my_empty_map": {},
"my_empty_vec": [],
"my_map": {
"": 0.0
},
"my_tuple": [
"💩",
42
],
"my_vec": [
"hello",
"world"
]
},
"myInt": 123,
"myNullableEnum": null
}
],
"type": "object",
"properties": {
"myInt": {
"type": "integer",
"format": "int32"
},
"myBool": {
"type": "boolean"
},
"myNullableEnum": true,
"myInnerStruct": {
"type": "object",
"properties": {
"my_map": {
"type": "object",
"additionalProperties": {
"type": "number",
"format": "double"
}
},
"my_vec": {
"type": "array",
"items": {
"type": "string"
}
},
"my_empty_map": {
"type": "object",
"additionalProperties": true
},
"my_empty_vec": {
"type": "array",
"items": true
},
"my_tuple": {
"type": "array",
"items": [
{
"type": "string",
"maxLength": 1,
"minLength": 1
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
],
"maxItems": 2,
"minItems": 2
}
}
}
}
}

View file

@ -0,0 +1,80 @@
{
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
"title": "MyStruct",
"type": "object",
"properties": {
"myInt": {
"type": "integer",
"format": "int32"
},
"myBool": {
"type": "boolean"
},
"myNullableEnum": {
"nullable": true
},
"myInnerStruct": {
"type": "object",
"properties": {
"my_map": {
"type": "object",
"additionalProperties": {
"type": "number",
"format": "double"
}
},
"my_vec": {
"type": "array",
"items": {
"type": "string"
}
},
"my_empty_map": {
"type": "object",
"additionalProperties": true
},
"my_empty_vec": {
"type": "array",
"items": {}
},
"my_tuple": {
"type": "array",
"items": [
{
"type": "string",
"maxLength": 1,
"minLength": 1
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
],
"maxItems": 2,
"minItems": 2
}
}
}
},
"example": {
"myBool": true,
"myInnerStruct": {
"my_empty_map": {},
"my_empty_vec": [],
"my_map": {
"": 0.0
},
"my_tuple": [
"💩",
42
],
"my_vec": [
"hello",
"world"
]
},
"myInt": 123,
"myNullableEnum": null
}
}

View file

@ -0,0 +1,77 @@
mod util;
use std::collections::HashMap;
use schemars::gen::SchemaSettings;
use serde::Serialize;
use util::*;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MyStruct {
pub my_int: i32,
pub my_bool: bool,
pub my_nullable_enum: Option<MyEnum>,
pub my_inner_struct: MyInnerStruct,
#[serde(skip)]
pub skip: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub skip_if_none: Option<MyEnum>,
}
#[derive(Serialize)]
pub struct MyInnerStruct {
pub my_map: HashMap<String, f64>,
pub my_vec: Vec<&'static str>,
pub my_empty_map: HashMap<String, f64>,
pub my_empty_vec: Vec<&'static str>,
pub my_tuple: (char, u8),
}
#[derive(Serialize)]
pub enum MyEnum {
StringNewType(String),
StructVariant { floats: Vec<f32> },
}
fn make_value() -> MyStruct {
let mut value = MyStruct {
my_int: 123,
my_bool: true,
my_nullable_enum: None,
my_inner_struct: MyInnerStruct {
my_map: HashMap::new(),
my_vec: vec!["hello", "world"],
my_empty_map: HashMap::new(),
my_empty_vec: vec![],
my_tuple: ('💩', 42),
},
skip: 123,
skip_if_none: None,
};
value.my_inner_struct.my_map.insert(String::new(), 0.0);
value
}
#[test]
fn schema_from_value_matches_draft07() -> TestResult {
let gen = SchemaSettings::draft07().into_generator();
let actual = gen.into_root_schema_for_value(&make_value())?;
test_schema(&actual, "from_value_draft07")
}
#[test]
fn schema_from_value_matches_2019_09() -> TestResult {
let gen = SchemaSettings::draft2019_09().into_generator();
let actual = gen.into_root_schema_for_value(&make_value())?;
test_schema(&actual, "from_value_2019_09")
}
#[test]
fn schema_from_value_matches_openapi3() -> TestResult {
let gen = SchemaSettings::openapi3().into_generator();
let actual = gen.into_root_schema_for_value(&make_value())?;
test_schema(&actual, "from_value_openapi3")
}

View file

@ -0,0 +1,5 @@
use schemars::schema_for;
fn main() {
let _schema = schema_for!(123);
}

View file

@ -0,0 +1,7 @@
error: This argument to `schema_for!` is not a type - did you mean to use `schema_for_value!` instead?
--> $DIR/schema_for_arg_value.rs:4:19
|
4 | let _schema = schema_for!(123);
| ^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -18,7 +18,7 @@ pub fn test_default_generated_schema<T: JsonSchema>(file: &str) -> TestResult {
test_schema(&actual, file)
}
fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
Ok(j) => j,
Err(e) => {