Skip to main content

rullst_orm/
types.rs

1use std::ops::{Deref, DerefMut};
2
3/// A wrapper for JSON columns in the database.
4/// This type allows users to easily cast a column to a struct that implements Serialize and Deserialize.
5#[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}