schemars/json_schema_impls/
maps.rs1use crate::_alloc_prelude::*;
2use crate::{json_schema, JsonSchema, Schema, SchemaGenerator};
3use alloc::borrow::Cow;
4use alloc::collections::{BTreeMap, BTreeSet};
5use serde_json::{Map, Value};
6
7#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
8enum IntegerSupport {
9 None,
10 Unsigned,
11 Signed,
12}
13
14impl<K, V> JsonSchema for BTreeMap<K, V>
15where
16 K: JsonSchema,
17 V: JsonSchema,
18{
19 inline_schema!();
20
21 fn schema_name() -> Cow<'static, str> {
22 Cow::Owned(if K::schema_id() == <str>::schema_id() {
23 format!("Map_of_{}", V::schema_name())
24 } else {
25 format!("Map_from_{}_to_{}", K::schema_name(), V::schema_name())
26 })
27 }
28
29 fn schema_id() -> Cow<'static, str> {
30 Cow::Owned(if K::schema_id() == <str>::schema_id() {
31 format!("Map<{}>", V::schema_id())
32 } else {
33 format!("Map<{}, {}>", K::schema_id(), V::schema_id())
34 })
35 }
36
37 fn json_schema(generator: &mut SchemaGenerator) -> Schema {
38 let key_schema = K::json_schema(generator);
39 let value_schema = generator.subschema_for::<V>();
40
41 let mut map_schema = json_schema!({
42 "type": "object",
43 });
44
45 let Some(mut options) = key_schema
46 .get("anyOf")
47 .and_then(Value::as_array)
48 .and_then(|a| a.iter().map(Value::as_object).collect::<Option<Vec<_>>>())
49 .or_else(|| Some(vec![key_schema.as_object()?]))
50 else {
51 return json_schema!({
52 "additionalProperties": value_schema,
53 "type": "object",
54 });
55 };
56
57 let prefix = format!("#{}/", generator.definitions_path_stripped());
59 for option in &mut options {
60 if let Some(d) = option
61 .get("$ref")
62 .and_then(Value::as_str)
63 .and_then(|r| r.strip_prefix(&prefix))
64 .and_then(|r| generator.definitions().get(r))
65 .and_then(Value::as_object)
66 {
67 *option = d;
68 }
69 }
70
71 let mut additional_properties = false;
72 let mut support_integers = IntegerSupport::None;
73 let mut patterns = BTreeSet::new();
74 let mut properties = BTreeSet::new();
75 for option in options {
76 let key_pattern = option.get("pattern").and_then(Value::as_str);
77 let key_enum = option
78 .get("enum")
79 .and_then(Value::as_array)
80 .and_then(|a| a.iter().map(Value::as_str).collect::<Option<Vec<_>>>());
81 let key_type = option.get("type").and_then(Value::as_str);
82 let key_minimum = option.get("minimum").and_then(Value::as_u64);
83
84 match (key_pattern, key_enum, key_type) {
85 (Some(pattern), _, Some("string")) => {
86 patterns.insert(pattern);
87 }
88 (None, Some(enum_values), Some("string")) => {
89 for value in enum_values {
90 properties.insert(value);
91 }
92 }
93 (_, _, Some("integer")) if key_minimum == Some(0) => {
94 support_integers = support_integers.max(IntegerSupport::Unsigned);
95 }
96 (_, _, Some("integer")) => {
97 support_integers = support_integers.max(IntegerSupport::Signed);
98 }
99 _ => {
100 additional_properties = true;
101 }
102 }
103 }
104
105 if additional_properties {
106 map_schema.insert(
107 "additionalProperties".to_owned(),
108 value_schema.clone().to_value(),
109 );
110 } else {
111 map_schema.insert("additionalProperties".to_owned(), Value::Bool(false));
112 }
113
114 match support_integers {
115 IntegerSupport::None => {}
116 IntegerSupport::Unsigned => {
117 patterns.insert(r"^\d+$");
118 }
119 IntegerSupport::Signed => {
120 patterns.insert(r"^-?\d+$");
121 }
122 }
123
124 if !patterns.is_empty() {
125 let mut patterns_map = Map::new();
126
127 for pattern in patterns {
128 patterns_map.insert(pattern.to_owned(), value_schema.clone().to_value());
129 }
130
131 map_schema.insert("patternProperties".to_owned(), Value::Object(patterns_map));
132 }
133
134 if !properties.is_empty() {
135 let mut properties_map = Map::new();
136
137 for property in properties {
138 properties_map.insert(property.to_owned(), value_schema.clone().to_value());
139 }
140
141 map_schema.insert("properties".to_owned(), Value::Object(properties_map));
142 }
143
144 map_schema
145 }
146}
147
148#[cfg(feature = "std")]
149forward_impl!((<K: JsonSchema, V: JsonSchema, H> JsonSchema for std::collections::HashMap<K, V, H>) => BTreeMap<K, V>);