diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs index b914699..aa6b570 100644 --- a/schemars/src/_private.rs +++ b/schemars/src/_private.rs @@ -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( @@ -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(pub T); + +pub trait NoSerialize: Sized { + fn maybe_to_value(self) -> Option { + None + } +} + +impl NoSerialize for T {} + +impl MaybeSerializeWrapper { + pub fn maybe_to_value(self) -> Option { + serde_json::value::to_value(self.0).ok() + } +} diff --git a/schemars/tests/default.rs b/schemars/tests/default.rs index d91585a..0d68d0e 100644 --- a/schemars/tests/default.rs +++ b/schemars/tests/default.rs @@ -1,6 +1,5 @@ mod util; use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use util::*; fn is_default(value: &T) -> bool { @@ -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, @@ -37,9 +36,10 @@ 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")] @@ -47,6 +47,9 @@ pub struct MyStruct2 { pub my_bool: bool, } +#[derive(Default, JsonSchema, Debug)] +pub struct NotSerialize; + #[test] fn schema_default_values() -> TestResult { test_default_generated_schema::("default") diff --git a/schemars/tests/expected/default.json b/schemars/tests/expected/default.json index 6210986..aefef83 100644 --- a/schemars/tests/expected/default.json +++ b/schemars/tests/expected/default.json @@ -22,6 +22,9 @@ }, "my_struct2_default_skipped": { "$ref": "#/definitions/MyStruct2" + }, + "not_serialize": { + "$ref": "#/definitions/NotSerialize" } }, "definitions": { @@ -38,6 +41,9 @@ "type": "boolean" } } + }, + "NotSerialize": { + "type": "null" } } } \ No newline at end of file diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs index aefe243..32dbf67 100644 --- a/schemars_derive/src/metadata.rs +++ b/schemars_derive/src/metadata.rs @@ -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)), }); }