Allow non-Serialize default values.
Default values that don't implement Serialize are now ignored, rather than causing a compile error.
This is done by simulating specialization using a technique copied from Rocket:
5ebefa97c9/core/lib/src/sentinel.rs (L391-L445)
Fixes #115
This commit is contained in:
parent
6f39a13724
commit
690fe44343
4 changed files with 46 additions and 4 deletions
|
@ -2,6 +2,8 @@ use crate::flatten::Merge;
|
||||||
use crate::gen::SchemaGenerator;
|
use crate::gen::SchemaGenerator;
|
||||||
use crate::schema::{Metadata, Schema, SchemaObject};
|
use crate::schema::{Metadata, Schema, SchemaObject};
|
||||||
use crate::JsonSchema;
|
use crate::JsonSchema;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
// Helper for generating schemas for flattened `Option` fields.
|
// Helper for generating schemas for flattened `Option` fields.
|
||||||
pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
|
pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
|
||||||
|
@ -32,3 +34,34 @@ pub fn apply_metadata(schema: Schema, metadata: Metadata) -> Schema {
|
||||||
Schema::Object(schema_obj)
|
Schema::Object(schema_obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hack to simulate specialization:
|
||||||
|
/// `MaybeSerializeWrapper(x).maybe_to_value()` will resolve to either
|
||||||
|
/// - The inherent method `MaybeSerializeWrapper::maybe_to_value(...)` if x is `Serialize`
|
||||||
|
/// - The trait method `NoSerialize::maybe_to_value(...)` from the blanket impl otherwise
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! _schemars_maybe_to_value {
|
||||||
|
($expression:expr) => {{
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use $crate::_private::{MaybeSerializeWrapper, NoSerialize as _};
|
||||||
|
|
||||||
|
MaybeSerializeWrapper($expression).maybe_to_value()
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MaybeSerializeWrapper<T>(pub T);
|
||||||
|
|
||||||
|
pub trait NoSerialize: Sized {
|
||||||
|
fn maybe_to_value(self) -> Option<Value> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> NoSerialize for T {}
|
||||||
|
|
||||||
|
impl<T: Serialize> MaybeSerializeWrapper<T> {
|
||||||
|
pub fn maybe_to_value(self) -> Option<Value> {
|
||||||
|
serde_json::value::to_value(self.0).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
mod util;
|
mod util;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
fn is_default<T: Default + PartialEq>(value: &T) -> bool {
|
fn is_default<T: Default + PartialEq>(value: &T) -> bool {
|
||||||
|
@ -25,7 +24,7 @@ where
|
||||||
ser.collect_str(&format_args!("i:{} b:{}", value.my_int, value.my_bool))
|
ser.collect_str(&format_args!("i:{} b:{}", value.my_int, value.my_bool))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize, JsonSchema, Debug)]
|
#[derive(Default, JsonSchema, Debug)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct MyStruct {
|
pub struct MyStruct {
|
||||||
pub my_int: i32,
|
pub my_int: i32,
|
||||||
|
@ -37,9 +36,10 @@ pub struct MyStruct {
|
||||||
skip_serializing_if = "is_default"
|
skip_serializing_if = "is_default"
|
||||||
)]
|
)]
|
||||||
pub my_struct2_default_skipped: MyStruct2,
|
pub my_struct2_default_skipped: MyStruct2,
|
||||||
|
pub not_serialize: NotSerialize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize, Serialize, JsonSchema, Debug, PartialEq)]
|
#[derive(Default, JsonSchema, Debug, PartialEq)]
|
||||||
#[serde(default = "ten_and_true")]
|
#[serde(default = "ten_and_true")]
|
||||||
pub struct MyStruct2 {
|
pub struct MyStruct2 {
|
||||||
#[serde(default = "six")]
|
#[serde(default = "six")]
|
||||||
|
@ -47,6 +47,9 @@ pub struct MyStruct2 {
|
||||||
pub my_bool: bool,
|
pub my_bool: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, JsonSchema, Debug)]
|
||||||
|
pub struct NotSerialize;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn schema_default_values() -> TestResult {
|
fn schema_default_values() -> TestResult {
|
||||||
test_default_generated_schema::<MyStruct>("default")
|
test_default_generated_schema::<MyStruct>("default")
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
},
|
},
|
||||||
"my_struct2_default_skipped": {
|
"my_struct2_default_skipped": {
|
||||||
"$ref": "#/definitions/MyStruct2"
|
"$ref": "#/definitions/MyStruct2"
|
||||||
|
},
|
||||||
|
"not_serialize": {
|
||||||
|
"$ref": "#/definitions/NotSerialize"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
@ -38,6 +41,9 @@
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"NotSerialize": {
|
||||||
|
"type": "null"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -68,7 +68,7 @@ impl<'a> SchemaMetadata<'a> {
|
||||||
|
|
||||||
if let Some(default) = &self.default {
|
if let Some(default) = &self.default {
|
||||||
setters.push(quote! {
|
setters.push(quote! {
|
||||||
default: #default.and_then(|d| schemars::_serde_json::value::to_value(d).ok()),
|
default: #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue