Skip to main content

good_ormning/runtime/
sqlite.rs

1#[cfg(feature = "chrono")]
2use chrono::{
3    DateTime,
4    Utc,
5    FixedOffset,
6};
7#[cfg(feature = "jiff")]
8use jiff::{
9    Zoned,
10    Timestamp,
11};
12
13pub trait GoodErrorQuery<T> {
14    fn to_good_error_query(self, query: &str) -> Result<T, loga::Error>;
15}
16
17impl<T> GoodErrorQuery<T> for Result<T, rusqlite::Error> {
18    fn to_good_error_query(self, query: &str) -> Result<T, loga::Error> {
19        match self {
20            Ok(v) => Ok(v),
21            Err(e) => Err(loga::err(e).context(format!("Error executing query: {}", query))),
22        }
23    }
24}
25
26pub trait SqliteConnection {
27    fn execute(&mut self, query: &str, params: impl rusqlite::Params) -> rusqlite::Result<usize>;
28    fn query<
29        T,
30        F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result<T>,
31    >(&mut self, query: &str, params: impl rusqlite::Params, f: F) -> rusqlite::Result<Vec<T>>;
32    fn load_array_module(&mut self) -> rusqlite::Result<()>;
33}
34
35impl SqliteConnection for rusqlite::Connection {
36    fn execute(&mut self, query: &str, params: impl rusqlite::Params) -> rusqlite::Result<usize> {
37        rusqlite::Connection::execute(self, query, params)
38    }
39
40    fn query<
41        T,
42        F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result<T>,
43    >(&mut self, query: &str, params: impl rusqlite::Params, mut f: F) -> rusqlite::Result<Vec<T>> {
44        let mut stmt = self.prepare(query)?;
45        let rows = stmt.query_map(params, |row| f(row))?;
46        let mut res = vec![];
47        for row in rows {
48            res.push(row?);
49        }
50        Ok(res)
51    }
52
53    fn load_array_module(&mut self) -> rusqlite::Result<()> {
54        rusqlite::vtab::array::load_module(self)
55    }
56}
57
58impl SqliteConnection for rusqlite::Transaction<'_> {
59    fn execute(&mut self, query: &str, params: impl rusqlite::Params) -> rusqlite::Result<usize> {
60        rusqlite::Connection::execute(self, query, params)
61    }
62
63    fn query<
64        T,
65        F: FnMut(&rusqlite::Row<'_>) -> rusqlite::Result<T>,
66    >(&mut self, query: &str, params: impl rusqlite::Params, mut f: F) -> rusqlite::Result<Vec<T>> {
67        let mut stmt = self.prepare(query)?;
68        let rows = stmt.query_map(params, |row| f(row))?;
69        let mut res = vec![];
70        for row in rows {
71            res.push(row?);
72        }
73        Ok(res)
74    }
75
76    fn load_array_module(&mut self) -> rusqlite::Result<()> {
77        // Assume loaded on connection
78        Ok(())
79    }
80}
81
82pub enum GoodOrmningSqliteTimestamp {
83    String(String),
84    I64(i64),
85}
86
87impl rusqlite::types::ToSql for GoodOrmningSqliteTimestamp {
88    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
89        match self {
90            GoodOrmningSqliteTimestamp::String(s) => Ok(
91                rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Text(s.clone())),
92            ),
93            GoodOrmningSqliteTimestamp::I64(i) => Ok(
94                rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(*i)),
95            ),
96        }
97    }
98}
99
100impl rusqlite::types::FromSql for GoodOrmningSqliteTimestamp {
101    fn column_result(value: rusqlite::types::ValueRef<'_>) -> Result<Self, rusqlite::types::FromSqlError> {
102        match value {
103            rusqlite::types::ValueRef::Text(s) => {
104                let s = std::str::from_utf8(s).map_err(|e| rusqlite::types::FromSqlError::Other(Box::new(e)))?;
105                Ok(GoodOrmningSqliteTimestamp::String(s.to_string()))
106            },
107            rusqlite::types::ValueRef::Integer(i) => {
108                Ok(GoodOrmningSqliteTimestamp::I64(i))
109            },
110            _ => Err(rusqlite::types::FromSqlError::InvalidType),
111        }
112    }
113}
114
115impl From<GoodOrmningSqliteTimestamp> for rusqlite::types::Value {
116    fn from(val: GoodOrmningSqliteTimestamp) -> Self {
117        match val {
118            GoodOrmningSqliteTimestamp::String(s) => rusqlite::types::Value::Text(s),
119            GoodOrmningSqliteTimestamp::I64(i) => rusqlite::types::Value::Integer(i),
120        }
121    }
122}
123
124pub trait GoodOrmningCustomAuto<T> {
125    fn to_sql(value: &T) -> i64;
126    fn from_sql(value: i64) -> Result<T, String>;
127}
128
129pub trait GoodOrmningCustomBool<T> {
130    fn to_sql(value: &T) -> bool;
131    fn from_sql(value: bool) -> Result<T, String>;
132}
133
134pub trait GoodOrmningCustomI16<T> {
135    fn to_sql(value: &T) -> i16;
136    fn from_sql(value: i16) -> Result<T, String>;
137}
138
139pub trait GoodOrmningCustomI32<T> {
140    fn to_sql(value: &T) -> i32;
141    fn from_sql(value: i32) -> Result<T, String>;
142}
143
144pub trait GoodOrmningCustomI64<T> {
145    fn to_sql(value: &T) -> i64;
146    fn from_sql(value: i64) -> Result<T, String>;
147}
148
149pub trait GoodOrmningCustomU32<T> {
150    fn to_sql(value: &T) -> u32;
151    fn from_sql(value: u32) -> Result<T, String>;
152}
153
154pub trait GoodOrmningCustomF32<T> {
155    fn to_sql(value: &T) -> f32;
156    fn from_sql(value: f32) -> Result<T, String>;
157}
158
159pub trait GoodOrmningCustomF64<T> {
160    fn to_sql(value: &T) -> f64;
161    fn from_sql(value: f64) -> Result<T, String>;
162}
163
164pub trait GoodOrmningCustomString<T> {
165    fn to_sql<'a>(value: &'a T) -> String;
166    fn from_sql(value: String) -> Result<T, String>;
167}
168
169pub trait GoodOrmningCustomBytes<T> {
170    fn to_sql<'a>(value: &'a T) -> Vec<u8>;
171    fn from_sql(value: Vec<u8>) -> Result<T, String>;
172}
173
174#[cfg(feature = "chrono")]
175pub trait GoodOrmningCustomUtcTimeChrono<T> {
176    fn to_sql(value: &T) -> DateTime<Utc>;
177    fn from_sql(value: DateTime<Utc>) -> Result<T, String>;
178}
179
180#[cfg(feature = "chrono")]
181pub trait GoodOrmningCustomFixedOffsetTimeChrono<T> {
182    fn to_sql(value: &T) -> DateTime<FixedOffset>;
183    fn from_sql(value: DateTime<FixedOffset>) -> Result<T, String>;
184}
185
186#[cfg(feature = "jiff")]
187pub trait GoodOrmningCustomUtcTimeJiff<T> {
188    fn to_sql(value: &T) -> Timestamp;
189    fn from_sql(value: Timestamp) -> Result<T, String>;
190}
191
192#[cfg(feature = "jiff")]
193pub trait GoodOrmningCustomFixedOffsetTimeJiff<T> {
194    fn to_sql(value: &T) -> Zoned;
195    fn from_sql(value: Zoned) -> Result<T, String>;
196}