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")
91 }
92 }
93}
94
95#[cfg(feature = "mysql")]
98mod mysql_impl {
99 use super::BindValue;
100 use sqlx::encode::{Encode, IsNull};
101 use sqlx::mysql::{MySql, MySqlTypeInfo};
102 use sqlx::Database;
103
104 impl<'q> Encode<'q, MySql> for BindValue {
105 fn encode_by_ref(
106 &self,
107 buf: &mut <MySql as Database>::ArgumentBuffer<'q>,
108 ) -> Result<IsNull, Box<dyn std::error::Error + Send + Sync>> {
109 Ok(match self {
110 BindValue::Null => <Option<i32> as Encode<MySql>>::encode_by_ref(&None, buf)?,
111 BindValue::Bool(b) => <i32 as Encode<MySql>>::encode_by_ref(&(*b as i32), buf)?,
112 BindValue::I64(n) => <i64 as Encode<MySql>>::encode_by_ref(n, buf)?,
113 BindValue::F64(n) => <f64 as Encode<MySql>>::encode_by_ref(n, buf)?,
114 BindValue::String(s) => <String as Encode<MySql>>::encode_by_ref(s, buf)?,
115 BindValue::Uuid(u) => {
116 let s = u.to_string();
117 <String as Encode<MySql>>::encode_by_ref(&s, buf)?
118 }
119 BindValue::Json(v) => {
120 let s = serde_json::to_string(v)
121 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
122 <String as Encode<MySql>>::encode_by_ref(&s, buf)?
123 }
124 })
125 }
126 }
127
128 impl sqlx::Type<MySql> for BindValue {
129 fn type_info() -> MySqlTypeInfo {
130 <String as sqlx::Type<MySql>>::type_info()
131 }
132 }
133}
134
135#[cfg(feature = "sqlite")]
138mod sqlite_impl {
139 use super::BindValue;
140 use sqlx::encode::{Encode, IsNull};
141 use sqlx::sqlite::{Sqlite, SqliteTypeInfo};
142 use sqlx::Database;
143
144 impl<'q> Encode<'q, Sqlite> for BindValue {
145 fn encode_by_ref(
146 &self,
147 buf: &mut <Sqlite as Database>::ArgumentBuffer<'q>,
148 ) -> Result<IsNull, Box<dyn std::error::Error + Send + Sync>> {
149 Ok(match self {
150 BindValue::Null => <Option<i32> as Encode<Sqlite>>::encode_by_ref(&None, buf)?,
151 BindValue::Bool(b) => <i32 as Encode<Sqlite>>::encode_by_ref(&(*b as i32), buf)?,
152 BindValue::I64(n) => <i64 as Encode<Sqlite>>::encode_by_ref(n, buf)?,
153 BindValue::F64(n) => <f64 as Encode<Sqlite>>::encode_by_ref(n, buf)?,
154 BindValue::String(s) => <String as Encode<Sqlite>>::encode_by_ref(s, buf)?,
155 BindValue::Uuid(u) => {
156 let s = u.to_string();
157 <String as Encode<Sqlite>>::encode_by_ref(&s, buf)?
158 }
159 BindValue::Json(v) => {
160 let s = serde_json::to_string(v)
161 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
162 <String as Encode<Sqlite>>::encode_by_ref(&s, buf)?
163 }
164 })
165 }
166 }
167
168 impl sqlx::Type<Sqlite> for BindValue {
169 fn type_info() -> SqliteTypeInfo {
170 <String as sqlx::Type<Sqlite>>::type_info()
171 }
172 }
173}
174
175#[cfg(feature = "postgres")]
177pub type PgBindValue = BindValue;