serenity/
json.rs

1//! This module exports different types for JSON interactions. It encapsulates the differences
2//! between serde_json and simd-json to allow ignoring those in the rest of the codebase.
3
4use std::borrow::Cow;
5use std::collections::HashMap;
6use std::hash::{BuildHasher, Hash};
7
8use serde::de::DeserializeOwned;
9#[cfg(test)]
10use serde::Deserialize;
11use serde::Serialize;
12
13use crate::Result;
14
15#[cfg(not(feature = "simd_json"))]
16mod export {
17    pub type Value = serde_json::Value;
18    pub type JsonMap = serde_json::Map<String, Value>;
19    pub const NULL: Value = Value::Null;
20
21    pub use serde_json::{json, Error as JsonError};
22}
23
24#[cfg(feature = "simd_json")]
25mod export {
26    pub type Value = simd_json::OwnedValue;
27    pub type JsonMap = simd_json::owned::Object;
28    pub const NULL: Value = Value::Static(simd_json::StaticNode::Null);
29
30    pub use simd_json::prelude::{
31        TypedContainerValue,
32        ValueAsContainer,
33        ValueAsMutContainer,
34        ValueAsScalar,
35    };
36    pub use simd_json::{json, Error as JsonError, StaticNode};
37}
38
39pub use export::*;
40
41#[cfg(feature = "http")]
42pub(crate) async fn decode_resp<T: serde::de::DeserializeOwned>(
43    resp: reqwest::Response,
44) -> Result<T> {
45    #[cfg(not(feature = "simd_json"))]
46    let result = serde_json::from_slice(&resp.bytes().await?)?;
47    #[cfg(feature = "simd_json")]
48    let result = simd_json::from_slice(&mut resp.bytes().await?.to_vec())?;
49    Ok(result)
50}
51
52/// Converts a HashMap into a final [`JsonMap`] representation.
53pub fn hashmap_to_json_map<H, T>(map: HashMap<T, Value, H>) -> JsonMap
54where
55    H: BuildHasher,
56    T: Eq + Hash + ToString,
57{
58    map.into_iter().map(|(k, v)| (k.to_string(), v)).collect()
59}
60
61/// Deserialize an instance of type `T` from a string of JSON text.
62///
63/// If the `simd_json` feature is enabled, this function turns its argument into `Cow::Owned`
64/// before deserializing from it. In other words, passing in a `&str` will result in a clone.
65#[allow(clippy::missing_errors_doc)]
66pub fn from_str<'a, T>(s: impl Into<Cow<'a, str>>) -> Result<T>
67where
68    T: DeserializeOwned,
69{
70    let s = s.into();
71    #[cfg(not(feature = "simd_json"))]
72    let result = serde_json::from_str(&s)?;
73    #[cfg(feature = "simd_json")]
74    let result = simd_json::from_slice(&mut s.into_owned().into_bytes())?;
75    Ok(result)
76}
77
78/// Deserialize an instance of type `T` from bytes of JSON text.
79#[allow(clippy::missing_errors_doc)]
80pub fn from_slice<T>(v: &[u8]) -> Result<T>
81where
82    T: DeserializeOwned,
83{
84    #[cfg(not(feature = "simd_json"))]
85    let result = serde_json::from_slice(v)?;
86    #[cfg(feature = "simd_json")]
87    // We clone here to obtain a mutable reference to the clone, since we don't have a mutable ref
88    // to the original.
89    let result = simd_json::from_slice(&mut v.to_vec())?;
90    Ok(result)
91}
92
93/// Interpret a [`Value`] as an instance of type `T`.
94#[allow(clippy::missing_errors_doc)]
95pub fn from_value<T>(value: Value) -> Result<T>
96where
97    T: DeserializeOwned,
98{
99    #[cfg(not(feature = "simd_json"))]
100    let result = serde_json::from_value(value)?;
101    #[cfg(feature = "simd_json")]
102    let result = simd_json::serde::from_owned_value(value)?;
103    Ok(result)
104}
105
106/// Deserialize an instance of type `T` from bytes of JSON text.
107#[allow(clippy::missing_errors_doc)]
108pub fn from_reader<R, T>(rdr: R) -> Result<T>
109where
110    R: std::io::Read,
111    T: DeserializeOwned,
112{
113    #[cfg(not(feature = "simd_json"))]
114    let result = serde_json::from_reader(rdr)?;
115    #[cfg(feature = "simd_json")]
116    let result = simd_json::from_reader(rdr)?;
117    Ok(result)
118}
119
120/// Serialize the given data structure as a String of JSON.
121#[allow(clippy::missing_errors_doc)]
122pub fn to_string<T>(value: &T) -> Result<String>
123where
124    T: ?Sized + Serialize,
125{
126    #[cfg(not(feature = "simd_json"))]
127    let result = serde_json::to_string(value)?;
128    #[cfg(feature = "simd_json")]
129    let result = simd_json::to_string(value)?;
130    Ok(result)
131}
132
133/// Serialize the given data structure as a pretty-printed String of JSON.
134#[allow(clippy::missing_errors_doc)]
135pub fn to_string_pretty<T>(value: &T) -> Result<String>
136where
137    T: ?Sized + Serialize,
138{
139    #[cfg(not(feature = "simd_json"))]
140    let result = serde_json::to_string_pretty(value)?;
141    #[cfg(feature = "simd_json")]
142    let result = simd_json::to_string_pretty(value)?;
143    Ok(result)
144}
145
146/// Serialize the given data structure as a JSON byte vector.
147#[allow(clippy::missing_errors_doc)]
148pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
149where
150    T: ?Sized + Serialize,
151{
152    #[cfg(not(feature = "simd_json"))]
153    let result = serde_json::to_vec(value)?;
154    #[cfg(feature = "simd_json")]
155    let result = simd_json::to_vec(value)?;
156    Ok(result)
157}
158
159/// Serialize the given data structure as a pretty-printed JSON byte vector.
160#[allow(clippy::missing_errors_doc)]
161pub fn to_vec_pretty<T>(value: &T) -> Result<Vec<u8>>
162where
163    T: ?Sized + Serialize,
164{
165    #[cfg(not(feature = "simd_json"))]
166    let result = serde_json::to_vec_pretty(value)?;
167    #[cfg(feature = "simd_json")]
168    let result = simd_json::to_vec_pretty(value)?;
169    Ok(result)
170}
171
172/// Convert a `T` into a [`Value`] which is an enum that can represent any valid JSON data.
173#[allow(clippy::missing_errors_doc)]
174pub fn to_value<T>(value: T) -> Result<Value>
175where
176    T: Serialize,
177{
178    #[cfg(not(feature = "simd_json"))]
179    let result = serde_json::to_value(value)?;
180    #[cfg(feature = "simd_json")]
181    let result = simd_json::serde::to_owned_value(value)?;
182    Ok(result)
183}
184
185#[cfg(test)]
186#[track_caller]
187pub(crate) fn assert_json<T>(data: &T, json: crate::json::Value)
188where
189    T: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug,
190{
191    // test serialization
192    let serialized = to_value(data).unwrap();
193    assert!(
194        serialized == json,
195        "data->JSON serialization failed\nexpected: {json:?}\n     got: {serialized:?}"
196    );
197
198    // test deserialization
199    let deserialized = from_value::<T>(json).unwrap();
200    assert!(
201        &deserialized == data,
202        "JSON->data deserialization failed\nexpected: {data:?}\n     got: {deserialized:?}"
203    );
204}