1use std::ops::{Deref, DerefMut};
2
3#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6#[serde(transparent)]
7pub struct Json<T>(pub T);
8
9impl<T> Deref for Json<T> {
10 type Target = T;
11
12 fn deref(&self) -> &Self::Target {
13 &self.0
14 }
15}
16
17impl<T> DerefMut for Json<T> {
18 fn deref_mut(&mut self) -> &mut Self::Target {
19 &mut self.0
20 }
21}
22
23#[cfg(not(any(
24 feature = "strict-postgres",
25 feature = "strict-mysql",
26 feature = "strict-sqlite"
27)))]
28impl<'r, T: serde::de::DeserializeOwned> sqlx::Decode<'r, sqlx::Any> for Json<T> {
29 fn decode(
30 value: sqlx::any::AnyValueRef<'r>,
31 ) -> Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
32 let text = <String as sqlx::Decode<sqlx::Any>>::decode(value)?;
33 let parsed: T = serde_json::from_str(&text)?;
34 Ok(Json(parsed))
35 }
36}
37
38#[cfg(not(any(
39 feature = "strict-postgres",
40 feature = "strict-mysql",
41 feature = "strict-sqlite"
42)))]
43impl<'q, T: serde::Serialize> sqlx::Encode<'q, sqlx::Any> for Json<T> {
44 fn encode_by_ref(
45 &self,
46 buf: &mut <sqlx::Any as sqlx::database::Database>::ArgumentBuffer,
47 ) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
48 let stringified = serde_json::to_string(&self.0)?;
49 <String as sqlx::Encode<sqlx::Any>>::encode(stringified, buf)
50 }
51}
52
53#[cfg(not(any(
54 feature = "strict-postgres",
55 feature = "strict-mysql",
56 feature = "strict-sqlite"
57)))]
58impl<T> sqlx::Type<sqlx::Any> for Json<T> {
59 fn type_info() -> sqlx::any::AnyTypeInfo {
60 <String as sqlx::Type<sqlx::Any>>::type_info()
61 }
62}
63
64#[cfg_attr(test, mutants::skip)]
65#[cfg(any(
66 feature = "strict-postgres",
67 feature = "strict-mysql",
68 feature = "strict-sqlite"
69))]
70impl<'r, T: serde::de::DeserializeOwned> sqlx::Decode<'r, crate::database::RullstDatabase>
71 for Json<T>
72{
73 fn decode(
74 value: <crate::database::RullstDatabase as sqlx::database::Database>::ValueRef<'r>,
75 ) -> Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
76 let text = <String as sqlx::Decode<crate::database::RullstDatabase>>::decode(value)?;
77 let parsed: T = serde_json::from_str(&text)?;
78 Ok(Json(parsed))
79 }
80}
81
82#[cfg_attr(test, mutants::skip)]
83#[cfg(any(
84 feature = "strict-postgres",
85 feature = "strict-mysql",
86 feature = "strict-sqlite"
87))]
88impl<'q, T: serde::Serialize> sqlx::Encode<'q, crate::database::RullstDatabase> for Json<T> {
89 fn encode_by_ref(
90 &self,
91 buf: &mut <crate::database::RullstDatabase as sqlx::database::Database>::ArgumentBuffer,
92 ) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
93 let stringified = serde_json::to_string(&self.0)?;
94 <String as sqlx::Encode<crate::database::RullstDatabase>>::encode(stringified, buf)
95 }
96}
97
98#[cfg_attr(test, mutants::skip)]
99#[cfg(any(
100 feature = "strict-postgres",
101 feature = "strict-mysql",
102 feature = "strict-sqlite"
103))]
104impl<T> sqlx::Type<crate::database::RullstDatabase> for Json<T> {
105 fn type_info() -> <crate::database::RullstDatabase as sqlx::database::Database>::TypeInfo {
106 <String as sqlx::Type<crate::database::RullstDatabase>>::type_info()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_json_deref() {
116 let mut j = Json(vec![1, 2, 3]);
117 assert_eq!(j.len(), 3);
118 j.push(4);
119 assert_eq!(j.len(), 4);
120 assert_eq!(j[0], 1);
121 }
122}