Further reduce memory footprint of SchemaObject.
More fields are now wrapped in Option<Box<_>>, reducing size of JsonSchema (depending on system) from 424 to 240 bytes.
This commit is contained in:
parent
72629a3c37
commit
c78d721fc5
6 changed files with 87 additions and 59 deletions
|
@ -36,9 +36,16 @@ macro_rules! impl_merge {
|
||||||
|
|
||||||
impl_merge!(SchemaObject {
|
impl_merge!(SchemaObject {
|
||||||
merge: definitions extensions instance_type enum_values
|
merge: definitions extensions instance_type enum_values
|
||||||
number string array object,
|
metadata subschemas number string array object,
|
||||||
or: schema id title description format const_value all_of any_of one_of not
|
or: format const_value reference,
|
||||||
if_schema then_schema else_schema reference,
|
});
|
||||||
|
|
||||||
|
impl_merge!(Metadata {
|
||||||
|
or: schema id title description,
|
||||||
|
});
|
||||||
|
|
||||||
|
impl_merge!(SubschemaValidation {
|
||||||
|
or: all_of any_of one_of not if_schema then_schema else_schema,
|
||||||
});
|
});
|
||||||
|
|
||||||
impl_merge!(NumberValidation {
|
impl_merge!(NumberValidation {
|
||||||
|
|
|
@ -124,30 +124,22 @@ impl SchemaGenerator {
|
||||||
self.definitions
|
self.definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> Result {
|
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> Result<SchemaObject> {
|
||||||
let schema = T::json_schema(self)?;
|
let mut schema: SchemaObject = T::json_schema(self)?.into();
|
||||||
Ok(match schema {
|
let metadata = schema.metadata.get_or_insert_with(Default::default);
|
||||||
Schema::Object(mut o) => {
|
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
||||||
o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
metadata.title = Some(T::schema_name());
|
||||||
o.title = Some(T::schema_name());
|
schema.definitions.extend(self.definitions().clone());
|
||||||
o.definitions.extend(self.definitions().clone());
|
Ok(schema)
|
||||||
Schema::Object(o)
|
|
||||||
}
|
|
||||||
schema => schema,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> Result {
|
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> Result<SchemaObject> {
|
||||||
let schema = T::json_schema(&mut self)?;
|
let mut schema: SchemaObject = T::json_schema(&mut self)?.into();
|
||||||
Ok(match schema {
|
let metadata = schema.metadata.get_or_insert_with(Default::default);
|
||||||
Schema::Object(mut o) => {
|
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
||||||
o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
metadata.title = Some(T::schema_name());
|
||||||
o.title = Some(T::schema_name());
|
schema.definitions.extend(self.into_definitions());
|
||||||
o.definitions.extend(self.into_definitions());
|
Ok(schema)
|
||||||
Schema::Object(o)
|
|
||||||
}
|
|
||||||
schema => schema,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dereference_once(&self, schema: Schema) -> Result<Schema> {
|
pub fn dereference_once(&self, schema: Schema) -> Result<Schema> {
|
||||||
|
|
|
@ -27,7 +27,10 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
|
||||||
},
|
},
|
||||||
) => Schema::Object(with_null_type(obj)),
|
) => Schema::Object(with_null_type(obj)),
|
||||||
schema => SchemaObject {
|
schema => SchemaObject {
|
||||||
any_of: Some(vec![schema, <()>::json_schema(gen)?]),
|
subschemas: Some(Box::new(SubschemaValidation {
|
||||||
|
any_of: Some(vec![schema, <()>::json_schema(gen)?]),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -99,7 +102,7 @@ mod tests {
|
||||||
Some(vec![InstanceType::Integer, InstanceType::Null].into())
|
Some(vec![InstanceType::Integer, InstanceType::Null].into())
|
||||||
);
|
);
|
||||||
assert_eq!(schema.extensions.get("nullable"), None);
|
assert_eq!(schema.extensions.get("nullable"), None);
|
||||||
assert_eq!(schema.any_of.is_none(), true);
|
assert_eq!(schema.subschemas.is_none(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -111,8 +114,8 @@ mod tests {
|
||||||
let schema = schema_object_for::<Option<Foo>>();
|
let schema = schema_object_for::<Option<Foo>>();
|
||||||
assert_eq!(schema.instance_type, None);
|
assert_eq!(schema.instance_type, None);
|
||||||
assert_eq!(schema.extensions.get("nullable"), None);
|
assert_eq!(schema.extensions.get("nullable"), None);
|
||||||
assert_eq!(schema.any_of.is_some(), true);
|
assert_eq!(schema.subschemas.is_some(), true);
|
||||||
let any_of = schema.any_of.unwrap();
|
let any_of = schema.subschemas.unwrap().any_of.unwrap();
|
||||||
assert_eq!(any_of.len(), 2);
|
assert_eq!(any_of.len(), 2);
|
||||||
assert_eq!(any_of[0], Schema::new_ref("#/definitions/Foo".to_string()));
|
assert_eq!(any_of[0], Schema::new_ref("#/definitions/Foo".to_string()));
|
||||||
assert_eq!(any_of[1], schema_for::<()>());
|
assert_eq!(any_of[1], schema_for::<()>());
|
||||||
|
@ -131,6 +134,6 @@ mod tests {
|
||||||
Some(SingleOrVec::from(InstanceType::Integer))
|
Some(SingleOrVec::from(InstanceType::Integer))
|
||||||
);
|
);
|
||||||
assert_eq!(schema.extensions.get("nullable"), Some(&json!(true)));
|
assert_eq!(schema.extensions.get("nullable"), Some(&json!(true)));
|
||||||
assert_eq!(schema.any_of.is_none(), true);
|
assert_eq!(schema.subschemas.is_none(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,14 +38,8 @@ impl From<bool> for Schema {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
||||||
#[serde(rename_all = "camelCase", default)]
|
#[serde(rename_all = "camelCase", default)]
|
||||||
pub struct SchemaObject {
|
pub struct SchemaObject {
|
||||||
#[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
pub schema: Option<String>,
|
pub metadata: Option<Box<Metadata>>,
|
||||||
#[serde(rename = "$id", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub id: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub title: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub description: Option<String>,
|
|
||||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||||
pub instance_type: Option<SingleOrVec<InstanceType>>,
|
pub instance_type: Option<SingleOrVec<InstanceType>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -54,23 +48,11 @@ pub struct SchemaObject {
|
||||||
pub enum_values: Option<Vec<Value>>,
|
pub enum_values: Option<Vec<Value>>,
|
||||||
#[serde(rename = "const", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "const", skip_serializing_if = "Option::is_none")]
|
||||||
pub const_value: Option<Value>,
|
pub const_value: Option<Value>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub all_of: Option<Vec<Schema>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub any_of: Option<Vec<Schema>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub one_of: Option<Vec<Schema>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub not: Option<Box<Schema>>,
|
|
||||||
#[serde(rename = "if", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub if_schema: Option<Box<Schema>>,
|
|
||||||
#[serde(rename = "then", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub then_schema: Option<Box<Schema>>,
|
|
||||||
#[serde(rename = "else", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub else_schema: Option<Box<Schema>>,
|
|
||||||
#[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")]
|
#[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")]
|
||||||
pub definitions: Map<String, Schema>,
|
pub definitions: Map<String, Schema>,
|
||||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
|
pub subschemas: Option<Box<SubschemaValidation>>,
|
||||||
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
pub number: Option<Box<NumberValidation>>,
|
pub number: Option<Box<NumberValidation>>,
|
||||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
pub string: Option<Box<StringValidation>>,
|
pub string: Option<Box<StringValidation>>,
|
||||||
|
@ -123,13 +105,48 @@ impl From<Schema> for SchemaObject {
|
||||||
Schema::Object(o) => o,
|
Schema::Object(o) => o,
|
||||||
Schema::Bool(true) => SchemaObject::default(),
|
Schema::Bool(true) => SchemaObject::default(),
|
||||||
Schema::Bool(false) => SchemaObject {
|
Schema::Bool(false) => SchemaObject {
|
||||||
not: Some(Schema::Object(Default::default()).into()),
|
subschemas: Some(Box::new(SubschemaValidation {
|
||||||
|
not: Some(Schema::Object(Default::default()).into()),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase", default)]
|
||||||
|
pub struct Metadata {
|
||||||
|
#[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub schema: Option<String>,
|
||||||
|
#[serde(rename = "$id", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub id: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase", default)]
|
||||||
|
pub struct SubschemaValidation {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub all_of: Option<Vec<Schema>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub any_of: Option<Vec<Schema>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub one_of: Option<Vec<Schema>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub not: Option<Box<Schema>>,
|
||||||
|
#[serde(rename = "if", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub if_schema: Option<Box<Schema>>,
|
||||||
|
#[serde(rename = "then", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub then_schema: Option<Box<Schema>>,
|
||||||
|
#[serde(rename = "else", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub else_schema: Option<Box<Schema>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
||||||
#[serde(rename_all = "camelCase", default)]
|
#[serde(rename_all = "camelCase", default)]
|
||||||
pub struct NumberValidation {
|
pub struct NumberValidation {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use schemars::{gen::SchemaSettings, schema::Schema, schema_for, JsonSchema};
|
use schemars::{gen::SchemaSettings, schema::SchemaObject, schema_for, JsonSchema};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::panic;
|
use std::panic;
|
||||||
|
@ -18,7 +18,7 @@ pub fn test_default_generated_schema<T: JsonSchema>(file: &str) -> TestResult {
|
||||||
test_schema(&actual, file)
|
test_schema(&actual, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_schema(actual: &Schema, file: &str) -> TestResult {
|
fn test_schema(actual: &SchemaObject, file: &str) -> TestResult {
|
||||||
let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
|
let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
|
||||||
Ok(j) => j,
|
Ok(j) => j,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -36,7 +36,7 @@ fn test_schema(actual: &Schema, file: &str) -> TestResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_actual_to_file(schema: &Schema, file: &str) -> TestResult {
|
fn write_actual_to_file(schema: &SchemaObject, file: &str) -> TestResult {
|
||||||
let actual_json = serde_json::to_string_pretty(&schema)?;
|
let actual_json = serde_json::to_string_pretty(&schema)?;
|
||||||
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
|
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -159,7 +159,10 @@ fn schema_for_external_tagged_enum<'a>(
|
||||||
}));
|
}));
|
||||||
|
|
||||||
wrap_schema_fields(quote! {
|
wrap_schema_fields(quote! {
|
||||||
any_of: Some(vec![#(#schemas),*]),
|
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
|
||||||
|
any_of: Some(vec![#(#schemas),*]),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +211,10 @@ fn schema_for_internal_tagged_enum<'a>(
|
||||||
});
|
});
|
||||||
|
|
||||||
wrap_schema_fields(quote! {
|
wrap_schema_fields(quote! {
|
||||||
any_of: Some(vec![#(#schemas),*]),
|
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
|
||||||
|
any_of: Some(vec![#(#schemas),*]),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +225,10 @@ fn schema_for_untagged_enum<'a>(
|
||||||
let schemas = variants.map(|v| schema_for_untagged_enum_variant(v, cattrs));
|
let schemas = variants.map(|v| schema_for_untagged_enum_variant(v, cattrs));
|
||||||
|
|
||||||
wrap_schema_fields(quote! {
|
wrap_schema_fields(quote! {
|
||||||
any_of: Some(vec![#(#schemas),*]),
|
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
|
||||||
|
any_of: Some(vec![#(#schemas),*]),
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue