1use std::ops::{Deref, DerefMut};
2
3use serde::de::DeserializeOwned;
4use serde::{Deserialize, Serialize};
5use sqlx::error::BoxDynError;
6use sqlx::sqlite::{Sqlite, SqliteTypeInfo, SqliteValueRef};
7
8use super::{DbValue, ToDbValue};
9
10#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(transparent)]
30pub struct Json<T>(pub T);
31
32impl<T> Deref for Json<T> {
33 type Target = T;
34
35 fn deref(&self) -> &T {
36 &self.0
37 }
38}
39
40impl<T> DerefMut for Json<T> {
41 fn deref_mut(&mut self) -> &mut T {
42 &mut self.0
43 }
44}
45
46impl<T> From<T> for Json<T> {
47 fn from(value: T) -> Self {
48 Json(value)
49 }
50}
51
52impl<T: Serialize> ToDbValue for Json<T> {
53 fn to_db_value(&self) -> DbValue {
54 DbValue::Json(serde_json::to_value(&self.0).unwrap_or(serde_json::Value::Null))
55 }
56}
57
58impl<T> sqlx::Type<Sqlite> for Json<T> {
59 fn type_info() -> SqliteTypeInfo {
60 <String as sqlx::Type<Sqlite>>::type_info()
61 }
62
63 fn compatible(ty: &SqliteTypeInfo) -> bool {
64 <String as sqlx::Type<Sqlite>>::compatible(ty)
65 }
66}
67
68impl<'r, T: DeserializeOwned> sqlx::Decode<'r, Sqlite> for Json<T> {
69 fn decode(value: SqliteValueRef<'r>) -> Result<Self, BoxDynError> {
70 let text = <String as sqlx::Decode<Sqlite>>::decode(value)?;
71 Ok(Json(serde_json::from_str(&text)?))
72 }
73}
74
75#[cfg(feature = "postgres")]
76mod postgres {
77 use serde::de::DeserializeOwned;
78 use sqlx::error::BoxDynError;
79 use sqlx::postgres::{PgTypeInfo, PgValueRef, Postgres};
80 use sqlx::{TypeInfo, ValueRef};
81
82 use super::Json;
83
84 impl<T> sqlx::Type<Postgres> for Json<T> {
85 fn type_info() -> PgTypeInfo {
86 PgTypeInfo::with_name("jsonb")
87 }
88
89 fn compatible(ty: &PgTypeInfo) -> bool {
90 matches!(ty.name(), "JSON" | "JSONB")
91 || <String as sqlx::Type<Postgres>>::compatible(ty)
92 }
93 }
94
95 impl<'r, T: DeserializeOwned> sqlx::Decode<'r, Postgres> for Json<T> {
96 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
97 let is_json = matches!(value.type_info().name(), "JSON" | "JSONB");
98 if is_json {
99 let sqlx::types::Json(inner) =
100 <sqlx::types::Json<T> as sqlx::Decode<Postgres>>::decode(value)?;
101 Ok(Json(inner))
102 } else {
103 let text = <String as sqlx::Decode<Postgres>>::decode(value)?;
104 Ok(Json(serde_json::from_str(&text)?))
105 }
106 }
107 }
108}