architect_sdk/sql/
params.rs1use serde_json::Value;
4
5#[derive(Clone, Debug)]
8pub enum BindValue {
9 Null,
10 Bool(bool),
11 I64(i64),
12 F64(f64),
13 String(String),
14 Uuid(uuid::Uuid),
15 Json(Value),
16}
17
18impl BindValue {
19 pub fn from_json(v: &Value) -> Result<Self, crate::error::AppError> {
20 Ok(match v {
21 Value::Null => BindValue::Null,
22 Value::Bool(b) => BindValue::Bool(*b),
23 Value::Number(n) => {
24 if let Some(i) = n.as_i64() {
25 BindValue::I64(i)
26 } else if let Some(f) = n.as_f64() {
27 BindValue::F64(f)
28 } else {
29 BindValue::I64(0)
30 }
31 }
32 Value::String(s) => {
33 if let Ok(u) = uuid::Uuid::parse_str(s) {
34 BindValue::Uuid(u)
35 } else {
36 BindValue::String(s.clone())
37 }
38 }
39 Value::Array(_) | Value::Object(_) => BindValue::Json(v.clone()),
40 })
41 }
42}
43
44#[cfg(feature = "postgres")]
47mod pg_impl {
48 use super::BindValue;
49 use sqlx::encode::{Encode, IsNull};
50 use sqlx::postgres::{PgTypeInfo, Postgres};
51 use sqlx::Database;
52
53 impl<'q> Encode<'q, Postgres> for BindValue {
54 fn encode_by_ref(
55 &self,
56 buf: &mut <Postgres as Database>::ArgumentBuffer<'q>,
57 ) -> Result<IsNull, Box<dyn std::error::Error + Send + Sync>> {
58 Ok(match self {
59 BindValue::Null => <Option<i32> as Encode<Postgres>>::encode_by_ref(&None, buf)?,
60 BindValue::Bool(b) => {
61 let s: &str = if *b { "true" } else { "false" };
62 <&str as Encode<Postgres>>::encode_by_ref(&s, buf)?
63 }
64 BindValue::I64(n) => {
65 let s = n.to_string();
66 <&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)?
67 }
68 BindValue::F64(n) => {
69 let s = format!("{}", n);
70 <&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)?
71 }
72 BindValue::String(s) => {
73 <&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)?
74 }
75 BindValue::Uuid(u) => {
76 let s = u.to_string();
77 <&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)?
78 }
79 BindValue::Json(v) => {
80 let s = serde_json::to_string(v)
81 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
82 <&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)?
83 }
84 })
85 }
86 }
87
88 impl sqlx::Type<Postgres> for BindValue {
89 fn type_info() -> PgTypeInfo {
90 PgTypeInfo::with_name("TEXT")
94 }
95
96 fn compatible(_ty: &PgTypeInfo) -> bool {
97 true
98 }
99 }
100}
101
102#[cfg(feature = "mysql")]
105mod mysql_impl {
106 use super::BindValue;
107 use sqlx::encode::{Encode, IsNull};
108 use sqlx::mysql::{MySql, MySqlTypeInfo};
109 use sqlx::Database;
110
111 impl<'q> Encode<'q, MySql> for BindValue {
112 fn encode_by_ref(
113 &self,
114 buf: &mut <MySql as Database>::ArgumentBuffer<'q>,
115 ) -> Result<IsNull, Box<dyn std::error::Error + Send + Sync>> {
116 Ok(match self {
117 BindValue::Null => <Option<i32> as Encode<MySql>>::encode_by_ref(&None, buf)?,
118 BindValue::Bool(b) => <i32 as Encode<MySql>>::encode_by_ref(&(*b as i32), buf)?,
119 BindValue::I64(n) => <i64 as Encode<MySql>>::encode_by_ref(n, buf)?,
120 BindValue::F64(n) => <f64 as Encode<MySql>>::encode_by_ref(n, buf)?,
121 BindValue::String(s) => <String as Encode<MySql>>::encode_by_ref(s, buf)?,
122 BindValue::Uuid(u) => {
123 let s = u.to_string();
124 <String as Encode<MySql>>::encode_by_ref(&s, buf)?
125 }
126 BindValue::Json(v) => {
127 let s = serde_json::to_string(v)
128 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
129 <String as Encode<MySql>>::encode_by_ref(&s, buf)?
130 }
131 })
132 }
133 }
134
135 impl sqlx::Type<MySql> for BindValue {
136 fn type_info() -> MySqlTypeInfo {
137 <String as sqlx::Type<MySql>>::type_info()
138 }
139 }
140}
141
142#[cfg(feature = "sqlite")]
145mod sqlite_impl {
146 use super::BindValue;
147 use sqlx::encode::{Encode, IsNull};
148 use sqlx::sqlite::{Sqlite, SqliteTypeInfo};
149 use sqlx::Database;
150
151 impl<'q> Encode<'q, Sqlite> for BindValue {
152 fn encode_by_ref(
153 &self,
154 buf: &mut <Sqlite as Database>::ArgumentBuffer<'q>,
155 ) -> Result<IsNull, Box<dyn std::error::Error + Send + Sync>> {
156 Ok(match self {
157 BindValue::Null => <Option<i32> as Encode<Sqlite>>::encode_by_ref(&None, buf)?,
158 BindValue::Bool(b) => <i32 as Encode<Sqlite>>::encode_by_ref(&(*b as i32), buf)?,
159 BindValue::I64(n) => <i64 as Encode<Sqlite>>::encode_by_ref(n, buf)?,
160 BindValue::F64(n) => <f64 as Encode<Sqlite>>::encode_by_ref(n, buf)?,
161 BindValue::String(s) => <String as Encode<Sqlite>>::encode_by_ref(s, buf)?,
162 BindValue::Uuid(u) => {
163 let s = u.to_string();
164 <String as Encode<Sqlite>>::encode_by_ref(&s, buf)?
165 }
166 BindValue::Json(v) => {
167 let s = serde_json::to_string(v)
168 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
169 <String as Encode<Sqlite>>::encode_by_ref(&s, buf)?
170 }
171 })
172 }
173 }
174
175 impl sqlx::Type<Sqlite> for BindValue {
176 fn type_info() -> SqliteTypeInfo {
177 <String as sqlx::Type<Sqlite>>::type_info()
178 }
179 }
180}
181
182#[cfg(feature = "postgres")]
184pub type PgBindValue = BindValue;