rainier_orm/json.rs
1//! [`Json<T>`] — a column whose Rust value is any `serde` type but whose
2//! storage is **JSON text on every dialect**.
3//!
4//! Databases disagree on JSON: MySQL/Postgres have a native `JSON` type, SQLite
5//! and Cloudflare D1 keep it in `TEXT`. Rather than special-case a `JSON`
6//! column type (and decode it differently per backend), `Json<T>` declares a
7//! plain `Text` column and (de)serializes `T` with `serde_json` at the column
8//! boundary. One representation — serialized JSON in a text column — works
9//! identically across MySQL, Postgres, SQLite, and D1, so a single generic
10//! adapter handles it everywhere. (If you have a native MySQL `JSON` column,
11//! migrate it to `TEXT`; the JSON text is byte-compatible.)
12//!
13//! ```ignore
14//! use rainier_orm::{Json, Entity};
15//! use serde::{Serialize, Deserialize};
16//!
17//! #[derive(Clone, Serialize, Deserialize)]
18//! struct Scopes(Vec<String>);
19//!
20//! #[derive(Entity)]
21//! struct Grant {
22//! #[orm(pk, auto_increment)]
23//! id: u64,
24//! scopes: Json<Scopes>, // stored as JSON text
25//! extra: Option<Json<serde_json::Value>>, // nullable, arbitrary JSON
26//! }
27//! ```
28
29use crate::{Error, Result, Row};
30use serde::{de::DeserializeOwned, Serialize};
31
32/// A `serde` value persisted as JSON text. Deref/`From` make it ergonomic to
33/// treat as the inner `T`.
34#[derive(Debug, Clone, PartialEq, Eq, Default)]
35pub struct Json<T>(pub T);
36
37impl<T> Json<T> {
38 pub fn into_inner(self) -> T {
39 self.0
40 }
41}
42
43impl<T> From<T> for Json<T> {
44 fn from(v: T) -> Self {
45 Json(v)
46 }
47}
48
49impl<T> core::ops::Deref for Json<T> {
50 type Target = T;
51 fn deref(&self) -> &T {
52 &self.0
53 }
54}
55
56impl<T> core::ops::DerefMut for Json<T> {
57 fn deref_mut(&mut self) -> &mut T {
58 &mut self.0
59 }
60}
61
62impl<T> crate::SqlType for Json<T> {
63 const COLUMN_TYPE: crate::ColumnType = crate::ColumnType::Text;
64}
65
66impl<T: Serialize> crate::ToColumn for Json<T> {
67 fn to_value(&self) -> sea_query::Value {
68 // Serialization failure is unexpected for a well-formed `T`; fall back
69 // to JSON null rather than panicking at the binding layer.
70 let s = serde_json::to_string(&self.0).unwrap_or_else(|_| "null".to_string());
71 sea_query::Value::String(Some(Box::new(s)))
72 }
73}
74
75impl<T: DeserializeOwned> crate::FromColumn for Json<T> {
76 fn from_column(row: &dyn Row, col: &str) -> Result<Self> {
77 let s =
78 row.get_string(col)?.ok_or_else(|| Error::msg(format!("column `{col}` was NULL")))?;
79 serde_json::from_str(&s)
80 .map(Json)
81 .map_err(|e| Error::msg(format!("column `{col}`: invalid JSON: {e}")))
82 }
83}