From e259955809dfee520ba0bcc71899be9c471616dd Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Sun, 17 May 2020 16:41:19 +0100 Subject: [PATCH] Attribute for defining examples (#23) --- schemars/tests/examples.rs | 25 +++++++++++++++++ schemars/tests/expected/examples.json | 39 +++++++++++++++++++++++++++ schemars_derive/src/attr/mod.rs | 8 +++++- schemars_derive/src/metadata.rs | 19 +++++++++++-- 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 schemars/tests/examples.rs create mode 100644 schemars/tests/expected/examples.json diff --git a/schemars/tests/examples.rs b/schemars/tests/examples.rs new file mode 100644 index 0000000..7ebca58 --- /dev/null +++ b/schemars/tests/examples.rs @@ -0,0 +1,25 @@ +mod util; +use schemars::JsonSchema; +use serde::Serialize; +use util::*; + +#[derive(Default, Debug, JsonSchema, Serialize)] +#[schemars(example = "Struct::default", example = "null")] +pub struct Struct { + #[schemars(example = "eight", example = "null")] + foo: i32, + bar: bool, + #[schemars(example = "null")] + baz: Option<&'static str>, +} + +fn eight() -> i32 { + 8 +} + +fn null() -> () {} + +#[test] +fn examples() -> TestResult { + test_default_generated_schema::("examples") +} diff --git a/schemars/tests/expected/examples.json b/schemars/tests/expected/examples.json new file mode 100644 index 0000000..8df965d --- /dev/null +++ b/schemars/tests/expected/examples.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Struct", + "examples": [ + { + "bar": false, + "baz": null, + "foo": 0 + }, + null + ], + "type": "object", + "required": [ + "bar", + "foo" + ], + "properties": { + "bar": { + "type": "boolean" + }, + "baz": { + "examples": [ + null + ], + "type": [ + "string", + "null" + ] + }, + "foo": { + "examples": [ + 8, + null + ], + "type": "integer", + "format": "int32" + } + } +} \ No newline at end of file diff --git a/schemars_derive/src/attr/mod.rs b/schemars_derive/src/attr/mod.rs index 4957e1c..9bf221c 100644 --- a/schemars_derive/src/attr/mod.rs +++ b/schemars_derive/src/attr/mod.rs @@ -16,7 +16,7 @@ pub struct Attrs { pub title: Option, pub description: Option, pub deprecated: bool, - // TODO pub example: Option, + pub examples: Vec, } #[derive(Debug)] @@ -111,6 +111,12 @@ impl Attrs { } } + Meta(NameValue(m)) if m.path.is_ident("example") => { + if let Ok(fun) = parse_lit_into_path(errors, attr_type, "example", &m.lit) { + self.examples.push(fun) + } + } + Meta(_meta_item) => { // TODO uncomment this for 0.8.0 (breaking change) // https://github.com/GREsau/schemars/issues/18 diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs index 4724223..3f616ee 100644 --- a/schemars_derive/src/metadata.rs +++ b/schemars_derive/src/metadata.rs @@ -3,13 +3,14 @@ use attr::Attrs; use proc_macro2::{Ident, Span, TokenStream}; use quote::{ToTokens, TokenStreamExt}; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct SchemaMetadata<'a> { pub title: Option<&'a str>, pub description: Option<&'a str>, pub deprecated: bool, pub read_only: bool, pub write_only: bool, + pub examples: &'a [syn::Path], pub default: Option, } @@ -36,7 +37,10 @@ impl<'a> SchemaMetadata<'a> { title: attrs.title.as_ref().and_then(none_if_empty), description: attrs.description.as_ref().and_then(none_if_empty), deprecated: attrs.deprecated, - ..Default::default() + examples: &attrs.examples, + read_only: false, + write_only: false, + default: None, } } @@ -80,6 +84,17 @@ impl<'a> SchemaMetadata<'a> { }); } + if !self.examples.is_empty() { + let examples = self.examples.iter().map(|eg| { + quote! { + schemars::_serde_json::value::to_value(#eg()) + } + }); + setters.push(quote! { + metadata.examples = vec![#(#examples),*].into_iter().flatten().collect(); + }); + } + if let Some(default) = &self.default { setters.push(quote! { metadata.default = #default.and_then(|d| schemars::_serde_json::value::to_value(d).ok());