Attribute for defining examples (#23)

This commit is contained in:
Graham Esau 2020-05-17 16:41:19 +01:00
parent 19b9bef395
commit e259955809
4 changed files with 88 additions and 3 deletions

View file

@ -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::<Struct>("examples")
}

View file

@ -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"
}
}
}

View file

@ -16,7 +16,7 @@ pub struct Attrs {
pub title: Option<String>, pub title: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub deprecated: bool, pub deprecated: bool,
// TODO pub example: Option<syn::Path>, pub examples: Vec<syn::Path>,
} }
#[derive(Debug)] #[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) => { Meta(_meta_item) => {
// TODO uncomment this for 0.8.0 (breaking change) // TODO uncomment this for 0.8.0 (breaking change)
// https://github.com/GREsau/schemars/issues/18 // https://github.com/GREsau/schemars/issues/18

View file

@ -3,13 +3,14 @@ use attr::Attrs;
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, TokenStreamExt}; use quote::{ToTokens, TokenStreamExt};
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub struct SchemaMetadata<'a> { pub struct SchemaMetadata<'a> {
pub title: Option<&'a str>, pub title: Option<&'a str>,
pub description: Option<&'a str>, pub description: Option<&'a str>,
pub deprecated: bool, pub deprecated: bool,
pub read_only: bool, pub read_only: bool,
pub write_only: bool, pub write_only: bool,
pub examples: &'a [syn::Path],
pub default: Option<TokenStream>, pub default: Option<TokenStream>,
} }
@ -36,7 +37,10 @@ impl<'a> SchemaMetadata<'a> {
title: attrs.title.as_ref().and_then(none_if_empty), title: attrs.title.as_ref().and_then(none_if_empty),
description: attrs.description.as_ref().and_then(none_if_empty), description: attrs.description.as_ref().and_then(none_if_empty),
deprecated: attrs.deprecated, 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 { if let Some(default) = &self.default {
setters.push(quote! { setters.push(quote! {
metadata.default = #default.and_then(|d| schemars::_serde_json::value::to_value(d).ok()); metadata.default = #default.and_then(|d| schemars::_serde_json::value::to_value(d).ok());