ruma_serde/canonical_json/
value.rs1use std::{
2 collections::BTreeMap,
3 convert::{TryFrom, TryInto},
4 fmt,
5};
6
7use js_int::Int;
8use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
9use serde_json::{to_string as to_json_string, Value as JsonValue};
10
11use super::Error;
12
13pub type Object = BTreeMap<String, CanonicalJsonValue>;
15
16#[derive(Clone, Eq, PartialEq)]
18#[allow(clippy::exhaustive_enums)]
19pub enum CanonicalJsonValue {
20 Null,
29
30 Bool(bool),
39
40 Integer(Int),
49
50 String(String),
59
60 Array(Vec<CanonicalJsonValue>),
69
70 Object(Object),
81}
82
83impl CanonicalJsonValue {
84 pub fn as_bool(&self) -> Option<bool> {
86 match self {
87 Self::Bool(b) => Some(*b),
88 _ => None,
89 }
90 }
91
92 pub fn as_integer(&self) -> Option<Int> {
94 match self {
95 Self::Integer(i) => Some(*i),
96 _ => None,
97 }
98 }
99
100 pub fn as_str(&self) -> Option<&str> {
102 match self {
103 Self::String(s) => Some(s),
104 _ => None,
105 }
106 }
107
108 pub fn as_array(&self) -> Option<&[CanonicalJsonValue]> {
110 match self {
111 Self::Array(a) => Some(a),
112 _ => None,
113 }
114 }
115
116 pub fn as_object(&self) -> Option<&Object> {
118 match self {
119 Self::Object(o) => Some(o),
120 _ => None,
121 }
122 }
123
124 pub fn as_array_mut(&mut self) -> Option<&mut Vec<CanonicalJsonValue>> {
126 match self {
127 Self::Array(a) => Some(a),
128 _ => None,
129 }
130 }
131
132 pub fn as_object_mut(&mut self) -> Option<&mut Object> {
134 match self {
135 Self::Object(o) => Some(o),
136 _ => None,
137 }
138 }
139
140 pub fn is_bool(&self) -> bool {
142 matches!(self, Self::Bool(_))
143 }
144
145 pub fn is_integer(&self) -> bool {
147 matches!(self, Self::Integer(_))
148 }
149
150 pub fn is_string(&self) -> bool {
152 matches!(self, Self::String(_))
153 }
154
155 pub fn is_array(&self) -> bool {
157 matches!(self, Self::Array(_))
158 }
159
160 pub fn is_object(&self) -> bool {
162 matches!(self, Self::Object(_))
163 }
164}
165
166impl Default for CanonicalJsonValue {
167 fn default() -> Self {
168 Self::Null
169 }
170}
171
172impl fmt::Debug for CanonicalJsonValue {
173 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
174 match *self {
175 Self::Null => formatter.debug_tuple("Null").finish(),
176 Self::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
177 Self::Integer(ref v) => fmt::Debug::fmt(v, formatter),
178 Self::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
179 Self::Array(ref v) => {
180 formatter.write_str("Array(")?;
181 fmt::Debug::fmt(v, formatter)?;
182 formatter.write_str(")")
183 }
184 Self::Object(ref v) => {
185 formatter.write_str("Object(")?;
186 fmt::Debug::fmt(v, formatter)?;
187 formatter.write_str(")")
188 }
189 }
190 }
191}
192
193impl fmt::Display for CanonicalJsonValue {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "{}", to_json_string(&self).map_err(|_| fmt::Error)?)
204 }
205}
206
207impl TryFrom<JsonValue> for CanonicalJsonValue {
208 type Error = Error;
209
210 fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
211 Ok(match val {
212 JsonValue::Bool(b) => Self::Bool(b),
213 JsonValue::Number(num) => Self::Integer(
214 Int::try_from(num.as_i64().ok_or(Error::IntConvert)?)
215 .map_err(|_| Error::IntConvert)?,
216 ),
217 JsonValue::Array(vec) => {
218 Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
219 }
220 JsonValue::String(string) => Self::String(string),
221 JsonValue::Object(obj) => Self::Object(
222 obj.into_iter()
223 .map(|(k, v)| Ok((k, v.try_into()?)))
224 .collect::<Result<Object, _>>()?,
225 ),
226 JsonValue::Null => Self::Null,
227 })
228 }
229}
230
231impl From<CanonicalJsonValue> for JsonValue {
232 fn from(val: CanonicalJsonValue) -> Self {
233 match val {
234 CanonicalJsonValue::Bool(b) => Self::Bool(b),
235 CanonicalJsonValue::Integer(int) => Self::Number(i64::from(int).into()),
236 CanonicalJsonValue::String(string) => Self::String(string),
237 CanonicalJsonValue::Array(vec) => {
238 Self::Array(vec.into_iter().map(Into::into).collect())
239 }
240 CanonicalJsonValue::Object(obj) => {
241 Self::Object(obj.into_iter().map(|(k, v)| (k, v.into())).collect())
242 }
243 CanonicalJsonValue::Null => Self::Null,
244 }
245 }
246}
247
248macro_rules! variant_impls {
249 ($variant:ident($ty:ty)) => {
250 impl From<$ty> for CanonicalJsonValue {
251 fn from(val: $ty) -> Self {
252 Self::$variant(val)
253 }
254 }
255
256 impl PartialEq<$ty> for CanonicalJsonValue {
257 fn eq(&self, other: &$ty) -> bool {
258 match self {
259 Self::$variant(val) => val == other,
260 _ => false,
261 }
262 }
263 }
264
265 impl PartialEq<CanonicalJsonValue> for $ty {
266 fn eq(&self, other: &CanonicalJsonValue) -> bool {
267 match other {
268 CanonicalJsonValue::$variant(val) => self == val,
269 _ => false,
270 }
271 }
272 }
273 };
274}
275
276variant_impls!(Bool(bool));
277variant_impls!(Integer(Int));
278variant_impls!(String(String));
279variant_impls!(Array(Vec<CanonicalJsonValue>));
280variant_impls!(Object(Object));
281
282impl Serialize for CanonicalJsonValue {
283 #[inline]
284 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
285 where
286 S: Serializer,
287 {
288 match self {
289 Self::Null => serializer.serialize_unit(),
290 Self::Bool(b) => serializer.serialize_bool(*b),
291 Self::Integer(n) => n.serialize(serializer),
292 Self::String(s) => serializer.serialize_str(s),
293 Self::Array(v) => v.serialize(serializer),
294 Self::Object(m) => {
295 use serde::ser::SerializeMap;
296 let mut map = serializer.serialize_map(Some(m.len()))?;
297 for (k, v) in m {
298 map.serialize_entry(k, v)?;
299 }
300 map.end()
301 }
302 }
303 }
304}
305
306impl<'de> Deserialize<'de> for CanonicalJsonValue {
307 #[inline]
308 fn deserialize<D>(deserializer: D) -> Result<CanonicalJsonValue, D::Error>
309 where
310 D: Deserializer<'de>,
311 {
312 let val = JsonValue::deserialize(deserializer)?;
313 val.try_into().map_err(serde::de::Error::custom)
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use std::convert::TryInto;
320
321 use serde_json::json;
322
323 use super::CanonicalJsonValue;
324
325 #[test]
326 fn to_string() {
327 const CANONICAL_STR: &str = r#"{"city":"London","street":"10 Downing Street"}"#;
328
329 let json: CanonicalJsonValue =
330 json!({ "city": "London", "street": "10 Downing Street" }).try_into().unwrap();
331
332 assert_eq!(format!("{}", json), CANONICAL_STR);
333 assert_eq!(format!("{:#}", json), CANONICAL_STR);
334 }
335}