Respect #[serde(transparent)] attribute (#17)
This commit is contained in:
parent
509a1c3b7b
commit
5a28cef598
8 changed files with 121 additions and 5 deletions
|
@ -2,6 +2,7 @@
|
||||||
## In-dev - version TBC
|
## In-dev - version TBC
|
||||||
### Added:
|
### Added:
|
||||||
- Setting `#[deprecated]` attribute will now cause generated schemas to have the `deprecated` property set to `true`
|
- Setting `#[deprecated]` attribute will now cause generated schemas to have the `deprecated` property set to `true`
|
||||||
|
- Respect #[serde(transparent)] attribute (https://github.com/GREsau/schemars/issues/17)
|
||||||
|
|
||||||
## [0.7.4] - 2020-05-16
|
## [0.7.4] - 2020-05-16
|
||||||
### Added:
|
### Added:
|
||||||
|
|
|
@ -133,6 +133,15 @@ Setting this on a container will set the `additionalProperties` keyword on gener
|
||||||
|
|
||||||
Serde docs: [container](https://serde.rs/container-attrs.html#deny_unknown_fields)
|
Serde docs: [container](https://serde.rs/container-attrs.html#deny_unknown_fields)
|
||||||
|
|
||||||
|
<h3 id="transparent">
|
||||||
|
|
||||||
|
`#[serde(transparent)]` / `#[schemars(transparent)]`
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
Set on a newtype struct or a braced struct with one field to make the struct's generated schema exactly the same as that of the single field's.
|
||||||
|
|
||||||
|
Serde docs: [container](https://serde.rs/container-attrs.html#transparent)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Other Attributes
|
## Other Attributes
|
||||||
|
|
33
schemars/tests/expected/transparent-struct.json
Normal file
33
schemars/tests/expected/transparent-struct.json
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "OuterStruct",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"inner": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/InnerStruct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"InnerStruct": {
|
||||||
|
"type": "array",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxItems": 2,
|
||||||
|
"minItems": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
schemars/tests/transparent.rs
Normal file
27
schemars/tests/transparent.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
mod util;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use util::*;
|
||||||
|
|
||||||
|
#[derive(Debug, JsonSchema)]
|
||||||
|
pub struct OuterStruct {
|
||||||
|
inner: TransparentStruct,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, JsonSchema)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct TransparentStruct {
|
||||||
|
#[serde(with = "TransparentNewType")]
|
||||||
|
inner: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, JsonSchema)]
|
||||||
|
#[schemars(transparent)]
|
||||||
|
pub struct TransparentNewType(Option<InnerStruct>);
|
||||||
|
|
||||||
|
#[derive(Debug, JsonSchema)]
|
||||||
|
pub struct InnerStruct(String, i32);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transparent_struct() -> TestResult {
|
||||||
|
test_default_generated_schema::<OuterStruct>("transparent-struct")
|
||||||
|
}
|
|
@ -49,6 +49,16 @@ impl<'a> Container<'a> {
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> String {
|
||||||
self.serde_attrs.name().deserialize_name()
|
self.serde_attrs.name().deserialize_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transparent_field(&'a self) -> Option<&'a Field> {
|
||||||
|
if self.serde_attrs.transparent() {
|
||||||
|
if let Data::Struct(_, fields) = &self.data {
|
||||||
|
return Some(&fields[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Variant<'a> {
|
impl<'a> Variant<'a> {
|
||||||
|
|
|
@ -19,6 +19,7 @@ static SERDE_KEYWORDS: &[&str] = &[
|
||||||
"skip_deserializing",
|
"skip_deserializing",
|
||||||
"flatten",
|
"flatten",
|
||||||
"remote",
|
"remote",
|
||||||
|
"transparent",
|
||||||
// Special cases - `with`/`serialize_with` are passed to serde but not copied from schemars attrs to serde attrs.
|
// Special cases - `with`/`serialize_with` are passed to serde but not copied from schemars attrs to serde attrs.
|
||||||
// This is because we want to preserve any serde attribute's `serialize_with` value to determine whether the field's
|
// This is because we want to preserve any serde attribute's `serialize_with` value to determine whether the field's
|
||||||
// default value should be serialized. We also check the `with` value on schemars/serde attrs e.g. to support deriving
|
// default value should be serialized. We also check the `with` value on schemars/serde attrs e.g. to support deriving
|
||||||
|
|
|
@ -30,10 +30,44 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
|
||||||
Err(e) => return compile_error(&e),
|
Err(e) => return compile_error(&e),
|
||||||
};
|
};
|
||||||
|
|
||||||
let schema_expr = schema_exprs::expr_for_container(&cont);
|
|
||||||
|
|
||||||
let type_name = &cont.ident;
|
let type_name = &cont.ident;
|
||||||
let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect();
|
let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
|
||||||
|
|
||||||
|
if let Some(transparent_field) = cont.transparent_field() {
|
||||||
|
let (ty, type_def) = schema_exprs::type_for_schema(transparent_field, 0);
|
||||||
|
return quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
|
||||||
|
#type_def
|
||||||
|
|
||||||
|
fn is_referenceable() -> bool {
|
||||||
|
<#ty as schemars::JsonSchema>::is_referenceable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schema_name() -> std::string::String {
|
||||||
|
<#ty as schemars::JsonSchema>::schema_name()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||||
|
<#ty as schemars::JsonSchema>::json_schema(gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema_for_flatten(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||||
|
<#ty as schemars::JsonSchema>::json_schema_for_flatten(gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_schema_as_property(
|
||||||
|
gen: &mut schemars::gen::SchemaGenerator,
|
||||||
|
parent: &mut schemars::schema::SchemaObject,
|
||||||
|
name: String,
|
||||||
|
metadata: Option<schemars::schema::Metadata>,
|
||||||
|
required: bool,
|
||||||
|
) {
|
||||||
|
<#ty as schemars::JsonSchema>::add_schema_as_property(gen, parent, name, metadata, required)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let mut schema_base_name = cont.name();
|
let mut schema_base_name = cont.name();
|
||||||
let schema_is_renamed = *type_name != schema_base_name;
|
let schema_is_renamed = *type_name != schema_base_name;
|
||||||
|
@ -46,6 +80,7 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect();
|
||||||
let schema_name = if type_params.is_empty() {
|
let schema_name = if type_params.is_empty() {
|
||||||
quote! {
|
quote! {
|
||||||
#schema_base_name.to_owned()
|
#schema_base_name.to_owned()
|
||||||
|
@ -67,7 +102,7 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
|
let schema_expr = schema_exprs::expr_for_container(&cont);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
|
|
|
@ -38,7 +38,7 @@ fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_for_schema(field: &Field, local_id: usize) -> (syn::Type, Option<TokenStream>) {
|
pub fn type_for_schema(field: &Field, local_id: usize) -> (syn::Type, Option<TokenStream>) {
|
||||||
match &field.attrs.with {
|
match &field.attrs.with {
|
||||||
None => (field.ty.to_owned(), None),
|
None => (field.ty.to_owned(), None),
|
||||||
Some(WithAttr::Type(ty)) => (ty.to_owned(), None),
|
Some(WithAttr::Type(ty)) => (ty.to_owned(), None),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue