Skip to main content

stefans_utils/
single_kv_map.rs

1#[cfg(feature = "schemars")]
2use schemars::{JsonSchema, json_schema};
3use serde::de::{Error, MapAccess, Visitor};
4use serde::ser::SerializeMap;
5use serde::{Deserialize, Deserializer, Serialize};
6use std::hash::Hash;
7use std::marker::PhantomData;
8
9#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)]
10pub struct SingleKVMap<Key, Value> {
11    pub key: Key,
12    pub value: Value,
13}
14
15impl<LeftKey: PartialEq<RightKey>, LeftValue: PartialEq<RightValue>, RightKey, RightValue>
16    PartialEq<SingleKVMap<RightKey, RightValue>> for SingleKVMap<LeftKey, LeftValue>
17{
18    fn eq(&self, other: &SingleKVMap<RightKey, RightValue>) -> bool {
19        self.key == other.key && self.value == other.value
20    }
21}
22
23impl<Key: Serialize, Value: Serialize> Serialize for SingleKVMap<Key, Value> {
24    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
25    where
26        S: serde::Serializer,
27    {
28        let mut map = serializer.serialize_map(Some(1))?;
29        map.serialize_entry(&self.key, &self.value)?;
30        map.end()
31    }
32}
33
34struct SingleKVMapVisitor<Key, Value>(PhantomData<(Key, Value)>);
35
36impl<Key, Value> Default for SingleKVMapVisitor<Key, Value> {
37    fn default() -> Self {
38        Self(PhantomData)
39    }
40}
41
42impl<'de, Key: Deserialize<'de>, Value: Deserialize<'de>> Visitor<'de>
43    for SingleKVMapVisitor<Key, Value>
44{
45    type Value = SingleKVMap<Key, Value>;
46
47    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
48        formatter.write_str("a map with a single key-value")
49    }
50
51    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error>
52    where
53        A::Error: Error,
54    {
55        let (key, value) = map
56            .next_entry()?
57            .ok_or(A::Error::missing_field("missing first (and only) entry"))?;
58
59        if let Some(remaining) = map.size_hint()
60            && remaining != 0
61        {
62            Err(A::Error::unknown_field(
63                &format!("Found {remaining} remaining entries, expecting 0"),
64                &[],
65            ))
66        } else {
67            Ok(SingleKVMap::new(key, value))
68        }
69    }
70}
71
72impl<'de, Key: Deserialize<'de>, Value: Deserialize<'de>> Deserialize<'de>
73    for SingleKVMap<Key, Value>
74{
75    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
76        deserializer.deserialize_map(SingleKVMapVisitor::default())
77    }
78}
79
80impl<Key, Value> SingleKVMap<Key, Value> {
81    pub fn new(key: Key, value: Value) -> Self {
82        Self { key, value }
83    }
84
85    pub fn from_entry(entry: (Key, Value)) -> Self {
86        let (key, value) = entry;
87        Self { key, value }
88    }
89
90    pub fn as_key(&self) -> &Key {
91        &self.key
92    }
93
94    pub fn as_key_mut(&mut self) -> &mut Key {
95        &mut self.key
96    }
97
98    pub fn into_key(self) -> Key {
99        self.key
100    }
101
102    pub fn set_key(&mut self, new_key: Key) -> Key {
103        std::mem::replace(&mut self.key, new_key)
104    }
105
106    pub fn as_value(&self) -> &Value {
107        &self.value
108    }
109
110    pub fn as_value_mut(&mut self) -> &mut Value {
111        &mut self.value
112    }
113
114    pub fn into_value(self) -> Value {
115        self.value
116    }
117
118    pub fn set_value(&mut self, new_value: Value) -> Value {
119        std::mem::replace(&mut self.value, new_value)
120    }
121
122    pub fn as_entry(&self) -> (&Key, &Value) {
123        (&self.key, &self.value)
124    }
125
126    pub fn as_entry_mut(&mut self) -> (&mut Key, &mut Value) {
127        (&mut self.key, &mut self.value)
128    }
129
130    pub fn into_entry(self) -> (Key, Value) {
131        (self.key, self.value)
132    }
133
134    pub fn set_entry(&mut self, new_entry: (Key, Value)) -> (Key, Value) {
135        let (new_key, new_value) = new_entry;
136        (
137            std::mem::replace(&mut self.key, new_key),
138            std::mem::replace(&mut self.value, new_value),
139        )
140    }
141}
142
143impl<Key, Value> From<(Key, Value)> for SingleKVMap<Key, Value> {
144    fn from(entry: (Key, Value)) -> Self {
145        Self::from_entry(entry)
146    }
147}
148
149impl<Key, Value> AsRef<SingleKVMap<Key, Value>> for SingleKVMap<Key, Value> {
150    fn as_ref(&self) -> &SingleKVMap<Key, Value> {
151        self
152    }
153}
154
155#[cfg(feature = "schemars")]
156impl<Key: JsonSchema, Value: JsonSchema> JsonSchema for SingleKVMap<Key, Value> {
157    fn schema_name() -> std::borrow::Cow<'static, str> {
158        format!("{}{}SingleKVMap", Key::schema_name(), Value::schema_name()).into()
159    }
160
161    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
162        let mut key_schema = generator.subschema_for::<Key>();
163        let value_schema = generator.subschema_for::<Value>();
164
165        key_schema.as_object_mut().map(|x| x.remove("type"));
166
167        json_schema!({
168            "type": "object",
169            "propertyNames": key_schema,
170            "additionalProperties": value_schema,
171            "minProperties": 1,
172            "maxProperties": 1,
173        })
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use pretty_assertions::assert_eq;
180    #[cfg(feature = "schemars")]
181    use schemars::schema_for;
182    use serde_json::json;
183
184    use super::*;
185
186    #[derive(Debug, Serialize, Deserialize, PartialEq)]
187    #[cfg_attr(feature = "schemars", derive(JsonSchema))]
188    enum MyEnum {
189        A,
190        B,
191    }
192
193    #[cfg(feature = "schemars")]
194    #[test]
195    fn derive_json_schema() {
196        assert_eq!(
197            schema_for!(SingleKVMap<i32, String>),
198            json_schema!({
199                "$schema": "https://json-schema.org/draft/2020-12/schema",
200                "title": "int32stringSingleKVMap",
201                "type": "object",
202                "additionalProperties": {
203                    "type": "string"
204                },
205                "propertyNames": {
206                    "format": "int32"
207                },
208                "minProperties": 1,
209                "maxProperties": 1,
210            })
211        )
212    }
213
214    #[cfg(feature = "schemars")]
215    #[test]
216    fn derive_json_schema_enum() {
217        assert_eq!(
218            schema_for!(SingleKVMap<MyEnum, MyEnum>),
219            json_schema!({
220                "$schema": "https://json-schema.org/draft/2020-12/schema",
221                "title": "MyEnumMyEnumSingleKVMap",
222                "type": "object",
223                "additionalProperties": {
224                    "$ref": "#/$defs/MyEnum"
225                },
226                "propertyNames": {
227                    "$ref": "#/$defs/MyEnum"
228                },
229                "minProperties": 1,
230                "maxProperties": 1,
231                "$defs": {
232                    "MyEnum": {
233                        "type": "string",
234                        "enum": ["A", "B"]
235                    }
236                }
237            })
238        )
239    }
240
241    #[test]
242    fn derive_serialize() {
243        assert_eq!(
244            serde_json::to_value(SingleKVMap::new(10, "hello")).unwrap(),
245            json! ({
246                "10": "hello"
247            })
248        )
249    }
250
251    #[test]
252    fn derive_serialize_enum() {
253        assert_eq!(
254            serde_json::to_value(SingleKVMap::new(MyEnum::A, MyEnum::B)).unwrap(),
255            json! ({
256                "A": "B"
257            })
258        )
259    }
260
261    #[test]
262    fn derive_deserialize() {
263        assert_eq!(
264            serde_json::from_value::<SingleKVMap<i32, String>>(json! ({
265                "10": "hello"
266            }))
267            .unwrap(),
268            SingleKVMap::new(10, "hello"),
269        )
270    }
271
272    #[test]
273    fn derive_deserialize_enum() {
274        assert_eq!(
275            serde_json::from_value::<SingleKVMap<MyEnum, MyEnum>>(json! ({
276                "A": "B"
277            }))
278            .unwrap(),
279            SingleKVMap::new(MyEnum::A, MyEnum::B),
280        )
281    }
282}