Skip to content

Commit

Permalink
Allow non-Serialize default values.
Browse files Browse the repository at this point in the history
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:
https://github.com/SergioBenitez/Rocket/blob/5ebefa97c992c37bdc476299304a339d429a43fc/core/lib/src/sentinel.rs#L391-L445

Fixes #115
  • Loading branch information
GREsau committed Nov 14, 2021
1 parent 6f39a13 commit 690fe44
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 4 deletions.
33 changes: 33 additions & 0 deletions schemars/src/_private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::flatten::Merge;
use crate::gen::SchemaGenerator;
use crate::schema::{Metadata, Schema, SchemaObject};
use crate::JsonSchema;
use serde::Serialize;
use serde_json::Value;

// Helper for generating schemas for flattened `Option` fields.
pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
Expand Down Expand Up @@ -32,3 +34,34 @@ pub fn apply_metadata(schema: Schema, metadata: Metadata) -> Schema {
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()
}
}
9 changes: 6 additions & 3 deletions schemars/tests/default.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
mod util;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use util::*;

fn is_default<T: Default + PartialEq>(value: &T) -> bool {
Expand All @@ -25,7 +24,7 @@ where
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)]
pub struct MyStruct {
pub my_int: i32,
Expand All @@ -37,16 +36,20 @@ pub struct MyStruct {
skip_serializing_if = "is_default"
)]
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")]
pub struct MyStruct2 {
#[serde(default = "six")]
pub my_int: i32,
pub my_bool: bool,
}

#[derive(Default, JsonSchema, Debug)]
pub struct NotSerialize;

#[test]
fn schema_default_values() -> TestResult {
test_default_generated_schema::<MyStruct>("default")
Expand Down
6 changes: 6 additions & 0 deletions schemars/tests/expected/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
},
"my_struct2_default_skipped": {
"$ref": "#/definitions/MyStruct2"
},
"not_serialize": {
"$ref": "#/definitions/NotSerialize"
}
},
"definitions": {
Expand All @@ -38,6 +41,9 @@
"type": "boolean"
}
}
},
"NotSerialize": {
"type": "null"
}
}
}
2 changes: 1 addition & 1 deletion schemars_derive/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl<'a> SchemaMetadata<'a> {

if let Some(default) = &self.default {
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)),
});
}

Expand Down

0 comments on commit 690fe44

Please sign in to comment.