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 {
|
||||
merge: definitions extensions instance_type enum_values
|
||||
number string array object,
|
||||
or: schema id title description format const_value all_of any_of one_of not
|
||||
if_schema then_schema else_schema reference,
|
||||
metadata subschemas number string array object,
|
||||
or: format const_value 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 {
|
||||
|
|
|
@ -124,30 +124,22 @@ impl SchemaGenerator {
|
|||
self.definitions
|
||||
}
|
||||
|
||||
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> Result {
|
||||
let schema = T::json_schema(self)?;
|
||||
Ok(match schema {
|
||||
Schema::Object(mut o) => {
|
||||
o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
||||
o.title = Some(T::schema_name());
|
||||
o.definitions.extend(self.definitions().clone());
|
||||
Schema::Object(o)
|
||||
}
|
||||
schema => schema,
|
||||
})
|
||||
pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> Result<SchemaObject> {
|
||||
let mut schema: SchemaObject = T::json_schema(self)?.into();
|
||||
let metadata = schema.metadata.get_or_insert_with(Default::default);
|
||||
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
||||
metadata.title = Some(T::schema_name());
|
||||
schema.definitions.extend(self.definitions().clone());
|
||||
Ok(schema)
|
||||
}
|
||||
|
||||
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> Result {
|
||||
let schema = T::json_schema(&mut self)?;
|
||||
Ok(match schema {
|
||||
Schema::Object(mut o) => {
|
||||
o.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
||||
o.title = Some(T::schema_name());
|
||||
o.definitions.extend(self.into_definitions());
|
||||
Schema::Object(o)
|
||||
}
|
||||
schema => schema,
|
||||
})
|
||||
pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> Result<SchemaObject> {
|
||||
let mut schema: SchemaObject = T::json_schema(&mut self)?.into();
|
||||
let metadata = schema.metadata.get_or_insert_with(Default::default);
|
||||
metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned());
|
||||
metadata.title = Some(T::schema_name());
|
||||
schema.definitions.extend(self.into_definitions());
|
||||
Ok(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 => 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()
|
||||
}
|
||||
.into(),
|
||||
|
@ -99,7 +102,7 @@ mod tests {
|
|||
Some(vec![InstanceType::Integer, InstanceType::Null].into())
|
||||
);
|
||||
assert_eq!(schema.extensions.get("nullable"), None);
|
||||
assert_eq!(schema.any_of.is_none(), true);
|
||||
assert_eq!(schema.subschemas.is_none(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -111,8 +114,8 @@ mod tests {
|
|||
let schema = schema_object_for::<Option<Foo>>();
|
||||
assert_eq!(schema.instance_type, None);
|
||||
assert_eq!(schema.extensions.get("nullable"), None);
|
||||
assert_eq!(schema.any_of.is_some(), true);
|
||||
let any_of = schema.any_of.unwrap();
|
||||
assert_eq!(schema.subschemas.is_some(), true);
|
||||
let any_of = schema.subschemas.unwrap().any_of.unwrap();
|
||||
assert_eq!(any_of.len(), 2);
|
||||
assert_eq!(any_of[0], Schema::new_ref("#/definitions/Foo".to_string()));
|
||||
assert_eq!(any_of[1], schema_for::<()>());
|
||||
|
@ -131,6 +134,6 @@ mod tests {
|
|||
Some(SingleOrVec::from(InstanceType::Integer))
|
||||
);
|
||||
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)]
|
||||
#[serde(rename_all = "camelCase", default)]
|
||||
pub struct SchemaObject {
|
||||
#[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>,
|
||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||
pub metadata: Option<Box<Metadata>>,
|
||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||
pub instance_type: Option<SingleOrVec<InstanceType>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -54,23 +48,11 @@ pub struct SchemaObject {
|
|||
pub enum_values: Option<Vec<Value>>,
|
||||
#[serde(rename = "const", skip_serializing_if = "Option::is_none")]
|
||||
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")]
|
||||
pub definitions: Map<String, Schema>,
|
||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||
pub subschemas: Option<Box<SubschemaValidation>>,
|
||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||
pub number: Option<Box<NumberValidation>>,
|
||||
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||
pub string: Option<Box<StringValidation>>,
|
||||
|
@ -123,13 +105,48 @@ impl From<Schema> for SchemaObject {
|
|||
Schema::Object(o) => o,
|
||||
Schema::Bool(true) => SchemaObject::default(),
|
||||
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()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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)]
|
||||
#[serde(rename_all = "camelCase", default)]
|
||||
pub struct NumberValidation {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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::fs;
|
||||
use std::panic;
|
||||
|
@ -18,7 +18,7 @@ pub fn test_default_generated_schema<T: JsonSchema>(file: &str) -> TestResult {
|
|||
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)) {
|
||||
Ok(j) => j,
|
||||
Err(e) => {
|
||||
|
@ -36,7 +36,7 @@ fn test_schema(actual: &Schema, file: &str) -> TestResult {
|
|||
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)?;
|
||||
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
|
||||
Ok(())
|
||||
|
|
|
@ -159,7 +159,10 @@ fn schema_for_external_tagged_enum<'a>(
|
|||
}));
|
||||
|
||||
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! {
|
||||
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));
|
||||
|
||||
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