From 2bc3957cb811c45b01cb04368a7263fdf303f94f Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 14 Oct 2019 20:00:57 +0100 Subject: [PATCH] Set readOnly/writeOnly on properties --- schemars/src/gen.rs | 4 ++-- schemars/src/schema.rs | 15 +++++++++++++ .../tests/expected/skip_struct_fields.json | 17 +++++++++----- schemars/tests/skip.rs | 21 +++++++----------- schemars_derive/src/lib.rs | 22 ++++++++++++++++--- 5 files changed, 55 insertions(+), 24 deletions(-) diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs index 725c3f4..b1b55b6 100644 --- a/schemars/src/gen.rs +++ b/schemars/src/gen.rs @@ -126,7 +126,7 @@ impl SchemaGenerator { pub fn root_schema_for(&mut self) -> Result { let mut schema: SchemaObject = T::json_schema(self)?.into(); - let metadata = schema.metadata.get_or_insert_with(Default::default); + let metadata = schema.metadata(); metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned()); metadata.title = Some(T::schema_name()); metadata.definitions.extend(self.definitions().clone()); @@ -135,7 +135,7 @@ impl SchemaGenerator { pub fn into_root_schema_for(mut self) -> Result { let mut schema: SchemaObject = T::json_schema(&mut self)?.into(); - let metadata = schema.metadata.get_or_insert_with(Default::default); + let metadata = schema.metadata(); metadata.schema = Some("http://json-schema.org/draft-07/schema#".to_owned()); metadata.title = Some(T::schema_name()); metadata.definitions.extend(self.into_definitions()); diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs index 9fde629..6299338 100644 --- a/schemars/src/schema.rs +++ b/schemars/src/schema.rs @@ -77,6 +77,14 @@ where } } +macro_rules! get_or_insert_default_fn { + ($name:ident, $ret:path) => { + pub fn $name(&mut self) -> &mut $ret { + self.$name.get_or_insert_with(Default::default) + } + }; +} + impl SchemaObject { pub fn new_ref(reference: String) -> Self { SchemaObject { @@ -95,6 +103,13 @@ impl SchemaObject { }; *self == only_ref } + + get_or_insert_default_fn!(metadata, Metadata); + get_or_insert_default_fn!(subschemas, SubschemaValidation); + get_or_insert_default_fn!(number, NumberValidation); + get_or_insert_default_fn!(string, StringValidation); + get_or_insert_default_fn!(array, ArrayValidation); + get_or_insert_default_fn!(object, ObjectValidation); } impl From for SchemaObject { diff --git a/schemars/tests/expected/skip_struct_fields.json b/schemars/tests/expected/skip_struct_fields.json index e47c0bf..3e78e1d 100644 --- a/schemars/tests/expected/skip_struct_fields.json +++ b/schemars/tests/expected/skip_struct_fields.json @@ -3,16 +3,21 @@ "title": "MyStruct", "type": "object", "required": [ - "included1", - "included2" + "included", + "writable" ], "properties": { - "included1": { + "included": { + "type": "null" + }, + "readable": { + "readOnly": true, + "type": "string" + }, + "writable": { + "writeOnly": true, "type": "number", "format": "float" - }, - "included2": { - "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/skip.rs b/schemars/tests/skip.rs index 19890b1..70bddb4 100644 --- a/schemars/tests/skip.rs +++ b/schemars/tests/skip.rs @@ -9,10 +9,10 @@ struct MyStruct { #[serde(skip)] skipped2: bool, #[serde(skip_deserializing)] - skipped3: String, + readable: String, #[serde(skip_serializing)] - included1: f32, - included2: (), + writable: f32, + included: (), } #[test] @@ -21,15 +21,11 @@ fn skip_struct_fields() -> TestResult { } #[derive(Debug, JsonSchema)] -struct TupleStruct ( - #[schemars(skip)] - i32, - #[serde(skip)] - bool, - #[serde(skip_deserializing)] - String, - #[serde(skip_serializing)] - f32, +struct TupleStruct( + #[schemars(skip)] i32, + #[serde(skip)] bool, + #[serde(skip_deserializing)] String, + #[serde(skip_serializing)] f32, (), ); @@ -55,4 +51,3 @@ pub enum MyEnum { fn skip_enum_variants() -> TestResult { test_default_generated_schema::("skip_enum_variants") } - diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index 4a01342..298d194 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -267,8 +267,9 @@ fn schema_for_tuple_struct(fields: &[Field]) -> TokenStream { fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream { let (flat, nested): (Vec<_>, Vec<_>) = fields .iter() - .filter(|f| !f.attrs.skip_deserializing()) + .filter(|f| !f.attrs.skip_deserializing() || !f.attrs.skip_serializing()) .partition(|f| f.attrs.flatten()); + let container_has_default = has_default(cattrs.default()); let mut required = Vec::new(); let recurse = nested.iter().map(|field| { @@ -277,8 +278,23 @@ fn schema_for_struct(fields: &[Field], cattrs: &attr::Container) -> TokenStream required.push(name.clone()); } let ty = get_json_schema_type(field); - quote_spanned! {field.original.span()=> - props.insert(#name.to_owned(), gen.subschema_for::<#ty>()?); + + if field.attrs.skip_deserializing() { + quote_spanned! {field.original.span()=> + let mut schema: schemars::schema::SchemaObject = gen.subschema_for::<#ty>()?.into(); + schema.metadata().read_only = true; + props.insert(#name.to_owned(), schema.into()); + } + } else if field.attrs.skip_serializing() { + quote_spanned! {field.original.span()=> + let mut schema: schemars::schema::SchemaObject = gen.subschema_for::<#ty>()?.into(); + schema.metadata().write_only = true; + props.insert(#name.to_owned(), schema.into()); + } + } else { + quote_spanned! {field.original.span()=> + props.insert(#name.to_owned(), gen.subschema_for::<#ty>()?); + } } });