Enable renaming schemas using attribute on struct

This commit is contained in:
Graham Esau 2019-08-07 23:03:54 +01:00
parent 67d44533d6
commit 1d0fd18c9e
6 changed files with 78 additions and 60 deletions

View file

@ -4,7 +4,7 @@
"type": "object", "type": "object",
"properties": { "properties": {
"inner": { "inner": {
"$ref": "#/definitions/MySimpleStruct" "$ref": "#/definitions/another-new-name"
}, },
"t": { "t": {
"type": "integer" "type": "integer"

View file

@ -1,9 +1,9 @@
mod util;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use schemars::{schema::*, schema_for, MakeSchema}; use schemars::{schema_for, MakeSchema};
use serde::{Deserialize, Serialize}; use util::*;
use std::error::Error;
#[derive(Serialize, Deserialize, Debug, PartialEq, MakeSchema)] #[derive(Debug, MakeSchema)]
struct Flat { struct Flat {
foo: f32, foo: f32,
bar: bool, bar: bool,
@ -11,7 +11,8 @@ struct Flat {
foobar: Vec<i32>, foobar: Vec<i32>,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, MakeSchema)] #[derive(Debug, MakeSchema)]
#[serde(rename = "Flat")]
struct Deep1 { struct Deep1 {
foo: f32, foo: f32,
#[serde(flatten)] #[serde(flatten)]
@ -19,26 +20,22 @@ struct Deep1 {
foobar: Vec<i32>, foobar: Vec<i32>,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, MakeSchema)] #[derive(Debug, MakeSchema)]
struct Deep2 { struct Deep2 {
bar: bool, bar: bool,
#[serde(flatten)] #[serde(flatten)]
deep3: Deep3, deep3: Deep3,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, MakeSchema)] #[derive(Debug, MakeSchema)]
struct Deep3 { struct Deep3 {
baz: String, baz: String,
} }
#[test] #[test]
fn flatten_schema() -> Result<(), Box<dyn Error>> { fn flatten_schema() -> TestResult {
let flat = schema_for!(Flat)?; let flat = schema_for!(Flat)?;
let mut deep = schema_for!(Deep1)?; let deep = schema_for!(Deep1)?;
match deep {
Schema::Object(ref mut o) => o.title = Some("Flat".to_owned()),
_ => assert!(false, "Schema was not object: {:?}", deep),
};
assert_eq!(flat, deep); assert_eq!(flat, deep);
Ok(()) Ok(())
} }

View file

@ -1,41 +1,39 @@
use schemars::MakeSchema;
mod util; mod util;
use schemars::MakeSchema;
use util::*; use util::*;
#[derive(MakeSchema)] #[derive(Debug, MakeSchema)]
pub struct MyStruct<T, U, V, W> { struct MyStruct<T, U, V, W> {
pub t: T, t: T,
pub u: U, u: U,
pub v: V, v: V,
pub w: W, w: W,
pub inner: MySimpleStruct, inner: MySimpleStruct,
} }
#[derive(MakeSchema)] #[derive(Debug, MakeSchema)]
pub struct MySimpleStruct {} struct MySimpleStruct {}
#[test] #[test]
fn default_name_multiple_type_params() -> TestResult { fn default_name_multiple_type_params() -> TestResult {
test_default_generated_schema::<MyStruct<i32, (), bool, Vec<String>>>("naming-default") test_default_generated_schema::<MyStruct<i32, (), bool, Vec<String>>>("naming-default")
} }
#[derive(MakeSchema)] #[derive(Debug, MakeSchema)]
#[serde(rename = "a-new-name-<W>-<T>-<T>")] #[serde(rename = "a-new-name-{W}-{T}-{T}")]
pub struct MyRenamedStruct<T, U, V, W> { struct MyRenamedStruct<T, U, V, W> {
pub t: T, t: T,
pub u: U, u: U,
pub v: V, v: V,
pub w: W, w: W,
pub inner: MySimpleRenamedStruct, inner: MySimpleRenamedStruct,
} }
#[derive(MakeSchema)] #[derive(Debug, MakeSchema)]
#[serde(rename = "another-new-name")] #[serde(rename = "another-new-name")]
pub struct MySimpleRenamedStruct {} struct MySimpleRenamedStruct {}
#[test] #[test]
#[ignore] // not yet implemented fn overriden_with_rename_multiple_type_params() -> TestResult {
fn overriden_with_rename_name_multiple_type_params() -> TestResult {
test_default_generated_schema::<MyRenamedStruct<i32, (), bool, Vec<String>>>("naming-custom") test_default_generated_schema::<MyRenamedStruct<i32, (), bool, Vec<String>>>("naming-custom")
} }

View file

@ -1,7 +1,6 @@
mod util;
use schemars::gen::SchemaSettings; use schemars::gen::SchemaSettings;
use schemars::schema::Schema; use schemars::schema::Schema;
mod util;
use util::*; use util::*;
#[test] #[test]

View file

@ -6,21 +6,7 @@ use std::panic;
pub type TestResult = Result<(), Box<dyn Error>>; pub type TestResult = Result<(), Box<dyn Error>>;
pub fn test_default_generated_schema<T: MakeSchema>(file: &str) -> TestResult { #[allow(dead_code)] // https://github.com/rust-lang/rust/issues/46379
let expected_json = fs::read_to_string(format!("tests/expected/{}.json", file))?;
let expected = serde_json::from_str(&expected_json)?;
let actual = schema_for!(T)?;
if actual != expected {
let actual_json = serde_json::to_string_pretty(&actual)?;
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
}
assert_eq!(actual, expected);
Ok(())
}
pub fn test_generated_schema<T: MakeSchema>(file: &str, settings: SchemaSettings) -> TestResult { pub fn test_generated_schema<T: MakeSchema>(file: &str, settings: SchemaSettings) -> TestResult {
let expected_json = fs::read_to_string(format!("tests/expected/{}.json", file))?; let expected_json = fs::read_to_string(format!("tests/expected/{}.json", file))?;
let expected = serde_json::from_str(&expected_json)?; let expected = serde_json::from_str(&expected_json)?;
@ -35,3 +21,19 @@ pub fn test_generated_schema<T: MakeSchema>(file: &str, settings: SchemaSettings
assert_eq!(actual, expected); assert_eq!(actual, expected);
Ok(()) Ok(())
} }
#[allow(dead_code)] // https://github.com/rust-lang/rust/issues/46379
pub fn test_default_generated_schema<T: MakeSchema>(file: &str) -> TestResult {
let expected_json = fs::read_to_string(format!("tests/expected/{}.json", file))?;
let expected = serde_json::from_str(&expected_json)?;
let actual = schema_for!(T)?;
if actual != expected {
let actual_json = serde_json::to_string_pretty(&actual)?;
fs::write(format!("tests/actual/{}.json", file), actual_json)?;
}
assert_eq!(actual, expected);
Ok(())
}

View file

@ -15,8 +15,10 @@ use syn::{DeriveInput, GenericParam, Generics};
#[proc_macro_derive(MakeSchema, attributes(schemars, serde))] #[proc_macro_derive(MakeSchema, attributes(schemars, serde))]
pub fn derive_make_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn derive_make_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut input = parse_macro_input!(input as DeriveInput); let mut input = parse_macro_input!(input as DeriveInput);
// TODO is mutating the input really the best way to do this? // TODO is mutating the input really the best way to do this?
add_trait_bounds(&mut input.generics); add_trait_bounds(&mut input.generics);
let ctxt = Ctxt::new(); let ctxt = Ctxt::new();
let cont = Container::from_ast(&ctxt, &input, Derive::Deserialize); let cont = Container::from_ast(&ctxt, &input, Derive::Deserialize);
if let Err(e) = ctxt.check() { if let Err(e) = ctxt.check() {
@ -29,20 +31,40 @@ pub fn derive_make_schema(input: proc_macro::TokenStream) -> proc_macro::TokenSt
_ => unimplemented!("work in progress!"), _ => unimplemented!("work in progress!"),
}; };
let name = cont.ident; let type_name = cont.ident;
let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect(); let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect();
let type_param_fmt = match type_params.len() {
0 => "{}".to_owned(), let schema_base_name = cont.attrs.name().deserialize_name();
n => format!("{{}}_For_{{}}{}", "_And_{}".repeat(n - 1)), let schema_name = if type_params.is_empty() {
quote! {
#schema_base_name.to_owned()
}
} else if schema_base_name == type_name.to_string() {
let mut schema_name_fmt = schema_base_name;
schema_name_fmt.push_str("_For_{}");
schema_name_fmt.push_str(&"_And_{}".repeat(type_params.len() - 1));
quote! {
format!(#schema_name_fmt #(,#type_params::schema_name())*)
}
} else {
let mut schema_name_fmt = schema_base_name;
for tp in &type_params {
schema_name_fmt.push_str(&format!("{{{}:.0}}", tp));
}
let fmt_param_names = &type_params;
let type_params = &type_params;
quote! {
format!(#schema_name_fmt #(,#fmt_param_names=#type_params::schema_name())*)
}
}; };
let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
let impl_block = quote! { let impl_block = quote! {
#[automatically_derived] #[automatically_derived]
impl #impl_generics schemars::MakeSchema for #name #ty_generics #where_clause { impl #impl_generics schemars::MakeSchema for #type_name #ty_generics #where_clause {
fn schema_name() -> String { fn schema_name() -> String {
format!(#type_param_fmt, stringify!(#name) #(,#type_params::schema_name())*) #schema_name
} }
fn make_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Result { fn make_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Result {