schemars/
_private.rs

1use crate::r#gen::SchemaGenerator;
2use crate::schema::{InstanceType, ObjectValidation, Schema, SchemaObject};
3use crate::{JsonSchema, Map, Set};
4use serde::Serialize;
5use serde_json::Value;
6
7// Helper for generating schemas for flattened `Option` fields.
8pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
9    generator: &mut SchemaGenerator,
10    required: bool,
11) -> Schema {
12    let mut schema = T::_schemars_private_non_optional_json_schema(generator);
13
14    if T::_schemars_private_is_option() && !required {
15        if let Schema::Object(SchemaObject {
16            object: Some(ref mut object_validation),
17            ..
18        }) = schema
19        {
20            object_validation.required.clear();
21        }
22    }
23
24    schema
25}
26
27/// Hack to simulate specialization:
28/// `MaybeSerializeWrapper(x).maybe_to_value()` will resolve to either
29/// - The inherent method `MaybeSerializeWrapper::maybe_to_value(...)` if x is `Serialize`
30/// - The trait method `NoSerialize::maybe_to_value(...)` from the blanket impl otherwise
31#[doc(hidden)]
32#[macro_export]
33macro_rules! _schemars_maybe_to_value {
34    ($expression:expr) => {{
35        #[allow(unused_imports)]
36        use $crate::_private::{MaybeSerializeWrapper, NoSerialize as _};
37
38        MaybeSerializeWrapper($expression).maybe_to_value()
39    }};
40}
41
42pub struct MaybeSerializeWrapper<T>(pub T);
43
44pub trait NoSerialize: Sized {
45    fn maybe_to_value(self) -> Option<Value> {
46        None
47    }
48}
49
50impl<T> NoSerialize for T {}
51
52impl<T: Serialize> MaybeSerializeWrapper<T> {
53    pub fn maybe_to_value(self) -> Option<Value> {
54        serde_json::value::to_value(self.0).ok()
55    }
56}
57
58/// Create a schema for a unit enum
59pub fn new_unit_enum(variant: &str) -> Schema {
60    Schema::Object(SchemaObject {
61        instance_type: Some(InstanceType::String.into()),
62        enum_values: Some(vec![variant.into()]),
63        ..SchemaObject::default()
64    })
65}
66
67/// Create a schema for an externally tagged enum
68pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema {
69    Schema::Object(SchemaObject {
70        instance_type: Some(InstanceType::Object.into()),
71        object: Some(Box::new(ObjectValidation {
72            properties: {
73                let mut props = Map::new();
74                props.insert(variant.to_owned(), sub_schema);
75                props
76            },
77            required: {
78                let mut required = Set::new();
79                required.insert(variant.to_owned());
80                required
81            },
82            // Externally tagged variants must prohibit additional
83            // properties irrespective of the disposition of
84            // `deny_unknown_fields`. If additional properties were allowed
85            // one could easily construct an object that validated against
86            // multiple variants since here it's the properties rather than
87            // the values of a property that distingish between variants.
88            additional_properties: Some(Box::new(false.into())),
89            ..Default::default()
90        })),
91        ..SchemaObject::default()
92    })
93}
94
95/// Create a schema for an internally tagged enum
96pub fn new_internally_tagged_enum(
97    tag_name: &str,
98    variant: &str,
99    deny_unknown_fields: bool,
100) -> Schema {
101    let tag_schema = Schema::Object(SchemaObject {
102        instance_type: Some(InstanceType::String.into()),
103        enum_values: Some(vec![variant.into()]),
104        ..Default::default()
105    });
106    Schema::Object(SchemaObject {
107        instance_type: Some(InstanceType::Object.into()),
108        object: Some(Box::new(ObjectValidation {
109            properties: {
110                let mut props = Map::new();
111                props.insert(tag_name.to_owned(), tag_schema);
112                props
113            },
114            required: {
115                let mut required = Set::new();
116                required.insert(tag_name.to_owned());
117                required
118            },
119            additional_properties: deny_unknown_fields.then(|| Box::new(false.into())),
120            ..Default::default()
121        })),
122        ..SchemaObject::default()
123    })
124}
125
126pub fn insert_object_property<T: ?Sized + JsonSchema>(
127    obj: &mut ObjectValidation,
128    key: &str,
129    has_default: bool,
130    required: bool,
131    schema: Schema,
132) {
133    obj.properties.insert(key.to_owned(), schema);
134    if !has_default && (required || !T::_schemars_private_is_option()) {
135        obj.required.insert(key.to_owned());
136    }
137}
138
139pub mod metadata {
140    use crate::Schema;
141    use serde_json::Value;
142
143    macro_rules! add_metadata_fn {
144        ($method:ident, $name:ident, $ty:ty) => {
145            pub fn $method(schema: Schema, $name: impl Into<$ty>) -> Schema {
146                let value = $name.into();
147                if value == <$ty>::default() {
148                    schema
149                } else {
150                    let mut schema_obj = schema.into_object();
151                    schema_obj.metadata().$name = value.into();
152                    Schema::Object(schema_obj)
153                }
154            }
155        };
156    }
157
158    add_metadata_fn!(add_description, description, String);
159    add_metadata_fn!(add_id, id, String);
160    add_metadata_fn!(add_title, title, String);
161    add_metadata_fn!(add_deprecated, deprecated, bool);
162    add_metadata_fn!(add_read_only, read_only, bool);
163    add_metadata_fn!(add_write_only, write_only, bool);
164    add_metadata_fn!(add_default, default, Option<Value>);
165
166    pub fn add_examples<I: IntoIterator<Item = Value>>(schema: Schema, examples: I) -> Schema {
167        let mut schema_obj = schema.into_object();
168        schema_obj.metadata().examples.extend(examples);
169        Schema::Object(schema_obj)
170    }
171}