ruma_common/canonical_json/
value.rs1use std::{collections::BTreeMap, fmt};
2
3use as_variant::as_variant;
4use js_int::{Int, UInt};
5use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
6use serde_json::{Value as JsonValue, to_string as to_json_string};
7
8use super::CanonicalJsonError;
9use crate::serde::{JsonCastable, JsonObject};
10
11pub type CanonicalJsonObject = BTreeMap<String, CanonicalJsonValue>;
13
14impl<T> JsonCastable<CanonicalJsonObject> for T where T: JsonCastable<JsonObject> {}
15
16#[derive(Clone, Default, Eq, PartialEq)]
18#[allow(clippy::exhaustive_enums)]
19pub enum CanonicalJsonValue {
20 #[default]
28 Null,
29
30 Bool(bool),
38
39 Integer(Int),
47
48 String(String),
56
57 Array(Vec<CanonicalJsonValue>),
65
66 Object(CanonicalJsonObject),
76}
77
78impl CanonicalJsonValue {
79 pub fn json_type(&self) -> CanonicalJsonType {
81 match self {
82 Self::Null => CanonicalJsonType::Null,
83 Self::Bool(_) => CanonicalJsonType::Boolean,
84 Self::Integer(_) => CanonicalJsonType::Integer,
85 Self::String(_) => CanonicalJsonType::String,
86 Self::Array(_) => CanonicalJsonType::Array,
87 Self::Object(_) => CanonicalJsonType::Object,
88 }
89 }
90
91 pub fn as_bool(&self) -> Option<bool> {
93 as_variant!(self, Self::Bool).copied()
94 }
95
96 pub fn as_integer(&self) -> Option<Int> {
98 as_variant!(self, Self::Integer).copied()
99 }
100
101 pub fn as_str(&self) -> Option<&str> {
103 as_variant!(self, Self::String)
104 }
105
106 pub fn as_array(&self) -> Option<&[CanonicalJsonValue]> {
108 as_variant!(self, Self::Array)
109 }
110
111 pub fn as_object(&self) -> Option<&CanonicalJsonObject> {
113 as_variant!(self, Self::Object)
114 }
115
116 pub fn as_array_mut(&mut self) -> Option<&mut Vec<CanonicalJsonValue>> {
118 as_variant!(self, Self::Array)
119 }
120
121 pub fn as_object_mut(&mut self) -> Option<&mut CanonicalJsonObject> {
123 as_variant!(self, Self::Object)
124 }
125
126 pub fn is_bool(&self) -> bool {
128 matches!(self, Self::Bool(_))
129 }
130
131 pub fn is_integer(&self) -> bool {
133 matches!(self, Self::Integer(_))
134 }
135
136 pub fn is_string(&self) -> bool {
138 matches!(self, Self::String(_))
139 }
140
141 pub fn is_array(&self) -> bool {
143 matches!(self, Self::Array(_))
144 }
145
146 pub fn is_object(&self) -> bool {
148 matches!(self, Self::Object(_))
149 }
150}
151
152impl fmt::Debug for CanonicalJsonValue {
153 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
154 match *self {
155 Self::Null => formatter.debug_tuple("Null").finish(),
156 Self::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
157 Self::Integer(ref v) => fmt::Debug::fmt(v, formatter),
158 Self::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
159 Self::Array(ref v) => {
160 formatter.write_str("Array(")?;
161 fmt::Debug::fmt(v, formatter)?;
162 formatter.write_str(")")
163 }
164 Self::Object(ref v) => {
165 formatter.write_str("Object(")?;
166 fmt::Debug::fmt(v, formatter)?;
167 formatter.write_str(")")
168 }
169 }
170 }
171}
172
173impl fmt::Display for CanonicalJsonValue {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 write!(f, "{}", to_json_string(&self).map_err(|_| fmt::Error)?)
184 }
185}
186
187impl TryFrom<JsonValue> for CanonicalJsonValue {
188 type Error = CanonicalJsonError;
189
190 fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
191 Ok(match val {
192 JsonValue::Bool(b) => Self::Bool(b),
193 JsonValue::Number(num) => {
194 if !num.is_i64() && !num.is_u64() && num.is_f64() {
199 return Err(CanonicalJsonError::InvalidType("float".to_owned()));
200 }
201
202 Self::Integer(
203 num.as_i64()
204 .and_then(|num| Int::try_from(num).ok())
205 .ok_or(CanonicalJsonError::IntegerOutOfRange)?,
206 )
207 }
208 JsonValue::Array(vec) => {
209 Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
210 }
211 JsonValue::String(string) => Self::String(string),
212 JsonValue::Object(obj) => Self::Object(
213 obj.into_iter()
214 .map(|(k, v)| Ok((k, v.try_into()?)))
215 .collect::<Result<CanonicalJsonObject, _>>()?,
216 ),
217 JsonValue::Null => Self::Null,
218 })
219 }
220}
221
222impl From<CanonicalJsonValue> for JsonValue {
223 fn from(val: CanonicalJsonValue) -> Self {
224 match val {
225 CanonicalJsonValue::Bool(b) => Self::Bool(b),
226 CanonicalJsonValue::Integer(int) => Self::Number(i64::from(int).into()),
227 CanonicalJsonValue::String(string) => Self::String(string),
228 CanonicalJsonValue::Array(vec) => {
229 Self::Array(vec.into_iter().map(Into::into).collect())
230 }
231 CanonicalJsonValue::Object(obj) => {
232 Self::Object(obj.into_iter().map(|(k, v)| (k, v.into())).collect())
233 }
234 CanonicalJsonValue::Null => Self::Null,
235 }
236 }
237}
238
239impl<T> JsonCastable<CanonicalJsonValue> for T {}
240
241macro_rules! variant_impls {
242 ($variant:ident($ty:ty)) => {
243 impl From<$ty> for CanonicalJsonValue {
244 fn from(val: $ty) -> Self {
245 Self::$variant(val.into())
246 }
247 }
248
249 impl PartialEq<$ty> for CanonicalJsonValue {
250 fn eq(&self, other: &$ty) -> bool {
251 match self {
252 Self::$variant(val) => val == other,
253 _ => false,
254 }
255 }
256 }
257
258 impl PartialEq<CanonicalJsonValue> for $ty {
259 fn eq(&self, other: &CanonicalJsonValue) -> bool {
260 match other {
261 CanonicalJsonValue::$variant(val) => self == val,
262 _ => false,
263 }
264 }
265 }
266 };
267}
268
269variant_impls!(Bool(bool));
270variant_impls!(Integer(Int));
271variant_impls!(String(String));
272variant_impls!(String(&str));
273variant_impls!(Array(Vec<CanonicalJsonValue>));
274variant_impls!(Object(CanonicalJsonObject));
275
276impl From<UInt> for CanonicalJsonValue {
277 fn from(value: UInt) -> Self {
278 Self::Integer(value.into())
279 }
280}
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#[derive(Debug)]
319#[allow(clippy::exhaustive_enums)]
320pub enum CanonicalJsonType {
321 Object,
323
324 String,
326
327 Integer,
329
330 Array,
332
333 Boolean,
335
336 Null,
338}
339
340#[cfg(test)]
341mod tests {
342 use serde_json::json;
343
344 use super::CanonicalJsonValue;
345
346 #[test]
347 fn to_string() {
348 const CANONICAL_STR: &str = r#"{"city":"London","street":"10 Downing Street"}"#;
349
350 let json: CanonicalJsonValue =
351 json!({ "city": "London", "street": "10 Downing Street" }).try_into().unwrap();
352
353 assert_eq!(format!("{json}"), CANONICAL_STR);
354 assert_eq!(format!("{json:#}"), CANONICAL_STR);
355 }
356}