Reduce memory footprint of SchemaObject.
Nested validation structs are now wrapped in Option<Box<_>>, reducing size of JsonSchema (depending on system) from 688 to 424 bytes.
This commit is contained in:
parent
5a82498e28
commit
72629a3c37
12 changed files with 87 additions and 38 deletions
|
@ -13,10 +13,10 @@ impl<T> JsonSchema for [T; 0] {
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Result {
|
fn json_schema(_: &mut SchemaGenerator) -> Result {
|
||||||
Ok(SchemaObject {
|
Ok(SchemaObject {
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
instance_type: Some(InstanceType::Array.into()),
|
||||||
array: ArrayValidation {
|
array: Some(Box::new(ArrayValidation {
|
||||||
max_items: Some(0),
|
max_items: Some(0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
|
@ -36,12 +36,12 @@ macro_rules! array_impls {
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Result {
|
fn json_schema(gen: &mut SchemaGenerator) -> Result {
|
||||||
Ok(SchemaObject {
|
Ok(SchemaObject {
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
instance_type: Some(InstanceType::Array.into()),
|
||||||
array: ArrayValidation {
|
array: Some(Box::new(ArrayValidation {
|
||||||
items: Some(gen.subschema_for::<T>()?.into()),
|
items: Some(gen.subschema_for::<T>()?.into()),
|
||||||
max_items: Some($len),
|
max_items: Some($len),
|
||||||
min_items: Some($len),
|
min_items: Some($len),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}.into())
|
}.into())
|
||||||
}
|
}
|
||||||
|
@ -70,12 +70,13 @@ mod tests {
|
||||||
schema.instance_type,
|
schema.instance_type,
|
||||||
Some(SingleOrVec::from(InstanceType::Array))
|
Some(SingleOrVec::from(InstanceType::Array))
|
||||||
);
|
);
|
||||||
|
let array_validation = schema.array.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
schema.array.items,
|
array_validation.items,
|
||||||
Some(SingleOrVec::from(schema_for::<i32>()))
|
Some(SingleOrVec::from(schema_for::<i32>()))
|
||||||
);
|
);
|
||||||
assert_eq!(schema.array.max_items, Some(8));
|
assert_eq!(array_validation.max_items, Some(8));
|
||||||
assert_eq!(schema.array.min_items, Some(8));
|
assert_eq!(array_validation.min_items, Some(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
// SomeStruct does not implement JsonSchema
|
// SomeStruct does not implement JsonSchema
|
||||||
|
@ -88,6 +89,7 @@ mod tests {
|
||||||
schema.instance_type,
|
schema.instance_type,
|
||||||
Some(SingleOrVec::from(InstanceType::Array))
|
Some(SingleOrVec::from(InstanceType::Array))
|
||||||
);
|
);
|
||||||
assert_eq!(schema.array.max_items, Some(0));
|
let array_validation = schema.array.unwrap();
|
||||||
|
assert_eq!(array_validation.max_items, Some(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,10 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
|
||||||
};
|
};
|
||||||
Ok(schema)
|
Ok(schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn json_schema_non_null(gen: &mut SchemaGenerator) -> Result {
|
||||||
|
T::json_schema_non_null(gen)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_null_type(mut obj: SchemaObject) -> SchemaObject {
|
fn with_null_type(mut obj: SchemaObject) -> SchemaObject {
|
||||||
|
|
|
@ -27,10 +27,10 @@ macro_rules! map_impl {
|
||||||
};
|
};
|
||||||
Ok(SchemaObject {
|
Ok(SchemaObject {
|
||||||
instance_type: Some(InstanceType::Object.into()),
|
instance_type: Some(InstanceType::Object.into()),
|
||||||
object: ObjectValidation {
|
object: Some(Box::new(ObjectValidation {
|
||||||
additional_properties: Some(Box::new(additional_properties)),
|
additional_properties: Some(Box::new(additional_properties)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}.into())
|
}.into())
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ mod tests {
|
||||||
Some(SingleOrVec::from(InstanceType::Object))
|
Some(SingleOrVec::from(InstanceType::Object))
|
||||||
);
|
);
|
||||||
let additional_properties = schema.object
|
let additional_properties = schema.object
|
||||||
|
.unwrap()
|
||||||
.additional_properties
|
.additional_properties
|
||||||
.expect("additionalProperties field present");
|
.expect("additionalProperties field present");
|
||||||
assert_eq!(*additional_properties, Schema::Bool(true));
|
assert_eq!(*additional_properties, Schema::Bool(true));
|
||||||
|
@ -80,6 +81,7 @@ mod tests {
|
||||||
Some(SingleOrVec::from(InstanceType::Object))
|
Some(SingleOrVec::from(InstanceType::Object))
|
||||||
);
|
);
|
||||||
let additional_properties = schema.object
|
let additional_properties = schema.object
|
||||||
|
.unwrap()
|
||||||
.additional_properties
|
.additional_properties
|
||||||
.expect("additionalProperties field present");
|
.expect("additionalProperties field present");
|
||||||
assert_eq!(*additional_properties, Schema::Object(Default::default()));
|
assert_eq!(*additional_properties, Schema::Object(Default::default()));
|
||||||
|
@ -102,6 +104,7 @@ mod tests {
|
||||||
Some(SingleOrVec::from(InstanceType::Object))
|
Some(SingleOrVec::from(InstanceType::Object))
|
||||||
);
|
);
|
||||||
let additional_properties = schema.object
|
let additional_properties = schema.object
|
||||||
|
.unwrap()
|
||||||
.additional_properties
|
.additional_properties
|
||||||
.expect("additionalProperties field present");
|
.expect("additionalProperties field present");
|
||||||
assert_eq!(*additional_properties, schema_for::<i32>());
|
assert_eq!(*additional_properties, schema_for::<i32>());
|
||||||
|
|
|
@ -58,11 +58,11 @@ impl JsonSchema for char {
|
||||||
fn json_schema(_: &mut SchemaGenerator) -> Result {
|
fn json_schema(_: &mut SchemaGenerator) -> Result {
|
||||||
Ok(SchemaObject {
|
Ok(SchemaObject {
|
||||||
instance_type: Some(InstanceType::String.into()),
|
instance_type: Some(InstanceType::String.into()),
|
||||||
string: StringValidation {
|
string: Some(Box::new(StringValidation {
|
||||||
min_length: Some(1),
|
min_length: Some(1),
|
||||||
max_length: Some(1),
|
max_length: Some(1),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
|
|
|
@ -17,10 +17,10 @@ macro_rules! seq_impl {
|
||||||
fn json_schema(gen: &mut SchemaGenerator) -> Result {
|
fn json_schema(gen: &mut SchemaGenerator) -> Result {
|
||||||
Ok(SchemaObject {
|
Ok(SchemaObject {
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
instance_type: Some(InstanceType::Array.into()),
|
||||||
array: ArrayValidation {
|
array: Some(Box::new(ArrayValidation {
|
||||||
items: Some(gen.subschema_for::<T>()?.into()),
|
items: Some(gen.subschema_for::<T>()?.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}.into())
|
}.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,12 @@ macro_rules! tuple_impls {
|
||||||
];
|
];
|
||||||
Ok(SchemaObject {
|
Ok(SchemaObject {
|
||||||
instance_type: Some(InstanceType::Array.into()),
|
instance_type: Some(InstanceType::Array.into()),
|
||||||
array: ArrayValidation {
|
array: Some(Box::new(ArrayValidation {
|
||||||
items: Some(items.into()),
|
items: Some(items.into()),
|
||||||
max_items: Some($len),
|
max_items: Some($len),
|
||||||
min_items: Some($len),
|
min_items: Some($len),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}.into())
|
}.into())
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,15 @@ mod tests {
|
||||||
schema.instance_type,
|
schema.instance_type,
|
||||||
Some(SingleOrVec::from(InstanceType::Array))
|
Some(SingleOrVec::from(InstanceType::Array))
|
||||||
);
|
);
|
||||||
|
let array_validation = schema.array.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
schema.array.items,
|
array_validation.items,
|
||||||
Some(SingleOrVec::Vec(vec![
|
Some(SingleOrVec::Vec(vec![
|
||||||
schema_for::<i32>(),
|
schema_for::<i32>(),
|
||||||
schema_for::<bool>()
|
schema_for::<bool>()
|
||||||
]))
|
]))
|
||||||
);
|
);
|
||||||
assert_eq!(schema.array.max_items, Some(2));
|
assert_eq!(array_validation.max_items, Some(2));
|
||||||
assert_eq!(schema.array.min_items, Some(2));
|
assert_eq!(array_validation.min_items, Some(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,10 @@ pub trait JsonSchema {
|
||||||
fn schema_name() -> String;
|
fn schema_name() -> String;
|
||||||
|
|
||||||
fn json_schema(gen: &mut gen::SchemaGenerator) -> Result;
|
fn json_schema(gen: &mut gen::SchemaGenerator) -> Result;
|
||||||
|
|
||||||
|
fn json_schema_non_null(gen: &mut gen::SchemaGenerator) -> Result {
|
||||||
|
Self::json_schema(gen)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -70,20 +70,33 @@ pub struct SchemaObject {
|
||||||
pub else_schema: Option<Box<Schema>>,
|
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)]
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
pub number: NumberValidation,
|
pub number: Option<Box<NumberValidation>>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
pub string: StringValidation,
|
pub string: Option<Box<StringValidation>>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
pub array: ArrayValidation,
|
pub array: Option<Box<ArrayValidation>>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten, deserialize_with = "skip_if_default")]
|
||||||
pub object: ObjectValidation,
|
pub object: Option<Box<ObjectValidation>>,
|
||||||
#[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
|
||||||
pub reference: Option<String>,
|
pub reference: Option<String>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub extensions: Map<String, Value>,
|
pub extensions: Map<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn skip_if_default<'de, D, T>(deserializer: D) -> Result<Option<Box<T>>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
T: Deserialize<'de> + Default + PartialEq,
|
||||||
|
{
|
||||||
|
let value = T::deserialize(deserializer)?;
|
||||||
|
if value == T::default() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(Box::new(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SchemaObject {
|
impl SchemaObject {
|
||||||
pub fn new_ref(reference: String) -> Self {
|
pub fn new_ref(reference: String) -> Self {
|
||||||
SchemaObject {
|
SchemaObject {
|
||||||
|
|
|
@ -4,7 +4,16 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"another-new-name": {
|
"another-new-name": {
|
||||||
"type": "object"
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"foo"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"foo": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
@ -4,7 +4,16 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"MySimpleStruct": {
|
"MySimpleStruct": {
|
||||||
"type": "object"
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"foo"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"foo": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
@ -12,7 +12,9 @@ struct MyStruct<T, U, V, W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, JsonSchema)]
|
#[derive(Debug, JsonSchema)]
|
||||||
struct MySimpleStruct {}
|
struct MySimpleStruct {
|
||||||
|
foo: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_name_multiple_type_params() -> TestResult {
|
fn default_name_multiple_type_params() -> TestResult {
|
||||||
|
@ -33,7 +35,9 @@ struct MyRenamedStruct<T, U, V, W> {
|
||||||
#[derive(Debug, JsonSchema)]
|
#[derive(Debug, JsonSchema)]
|
||||||
#[serde(rename = "this-attribute-is-ignored")]
|
#[serde(rename = "this-attribute-is-ignored")]
|
||||||
#[schemars(rename = "another-new-name")]
|
#[schemars(rename = "another-new-name")]
|
||||||
struct MySimpleRenamedStruct {}
|
struct MySimpleRenamedStruct {
|
||||||
|
foo: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn overriden_with_rename_multiple_type_params() -> TestResult {
|
fn overriden_with_rename_multiple_type_params() -> TestResult {
|
||||||
|
|
|
@ -142,7 +142,7 @@ fn schema_for_external_tagged_enum<'a>(
|
||||||
let sub_schema = schema_for_untagged_enum_variant(variant, cattrs);
|
let sub_schema = schema_for_untagged_enum_variant(variant, cattrs);
|
||||||
wrap_schema_fields(quote! {
|
wrap_schema_fields(quote! {
|
||||||
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
||||||
object: schemars::schema::ObjectValidation {
|
object: Some(Box::new(schemars::schema::ObjectValidation {
|
||||||
properties: {
|
properties: {
|
||||||
let mut props = schemars::Map::new();
|
let mut props = schemars::Map::new();
|
||||||
props.insert(#name.to_owned(), #sub_schema);
|
props.insert(#name.to_owned(), #sub_schema);
|
||||||
|
@ -154,7 +154,7 @@ fn schema_for_external_tagged_enum<'a>(
|
||||||
required
|
required
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ fn schema_for_internal_tagged_enum<'a>(
|
||||||
});
|
});
|
||||||
let tag_schema = wrap_schema_fields(quote! {
|
let tag_schema = wrap_schema_fields(quote! {
|
||||||
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
||||||
object: schemars::schema::ObjectValidation {
|
object: Some(Box::new(schemars::schema::ObjectValidation {
|
||||||
properties: {
|
properties: {
|
||||||
let mut props = schemars::Map::new();
|
let mut props = schemars::Map::new();
|
||||||
props.insert(#tag_name.to_owned(), #type_schema);
|
props.insert(#tag_name.to_owned(), #type_schema);
|
||||||
|
@ -188,7 +188,7 @@ fn schema_for_internal_tagged_enum<'a>(
|
||||||
required
|
required
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
});
|
});
|
||||||
let variant_schema = match variant.style {
|
let variant_schema = match variant.style {
|
||||||
Style::Unit => return tag_schema,
|
Style::Unit => return tag_schema,
|
||||||
|
@ -275,7 +275,7 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream
|
||||||
|
|
||||||
let schema = wrap_schema_fields(quote! {
|
let schema = wrap_schema_fields(quote! {
|
||||||
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
||||||
object: schemars::schema::ObjectValidation {
|
object: Some(Box::new(schemars::schema::ObjectValidation {
|
||||||
properties: {
|
properties: {
|
||||||
let mut props = schemars::Map::new();
|
let mut props = schemars::Map::new();
|
||||||
#(#recurse)*
|
#(#recurse)*
|
||||||
|
@ -287,13 +287,13 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream
|
||||||
required
|
required
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
let flattens = flat.iter().map(|field| {
|
let flattens = flat.iter().map(|field| {
|
||||||
let ty = get_json_schema_type(field);
|
let ty = get_json_schema_type(field);
|
||||||
quote_spanned! {field.original.span()=>
|
quote_spanned! {field.original.span()=>
|
||||||
.flatten(<#ty>::json_schema(gen)?)?
|
.flatten(<#ty>::json_schema_non_null(gen)?)?
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue