tauri_plugin_sql/
wrapper.rs1#[cfg(feature = "sqlite")]
6use std::fs::create_dir_all;
7
8use indexmap::IndexMap;
9use serde_json::Value as JsonValue;
10#[cfg(any(feature = "sqlite", feature = "mysql", feature = "postgres"))]
11use sqlx::{Column, Executor, Pool, Row, migrate::MigrateDatabase};
12#[cfg(any(feature = "sqlite", feature = "mysql", feature = "postgres"))]
13use tauri::Manager;
14use tauri::{AppHandle, Runtime};
15
16#[cfg(feature = "mysql")]
17use sqlx::MySql;
18#[cfg(feature = "postgres")]
19use sqlx::Postgres;
20#[cfg(feature = "sqlite")]
21use sqlx::Sqlite;
22
23use crate::LastInsertId;
24
25pub enum DbPool {
31 #[cfg(feature = "sqlite")]
33 Sqlite(Pool<Sqlite>),
34 #[cfg(feature = "mysql")]
36 MySql(Pool<MySql>),
37 #[cfg(feature = "postgres")]
39 Postgres(Pool<Postgres>),
40 #[cfg(not(any(feature = "sqlite", feature = "mysql", feature = "postgres")))]
43 None,
44}
45
46impl DbPool {
78 pub(crate) async fn connect<R: Runtime>(
79 conn_url: &str,
80 _app: &AppHandle<R>,
81 ) -> Result<Self, crate::Error> {
82 match conn_url
83 .split_once(':')
84 .ok_or_else(|| crate::Error::InvalidDbUrl(conn_url.to_string()))?
85 .0
86 {
87 #[cfg(feature = "sqlite")]
88 "sqlite" => {
89 let app_path = _app
90 .path()
91 .app_config_dir()
92 .expect("No App config path was found!");
93
94 create_dir_all(&app_path).expect("Couldn't create app config dir");
95
96 let conn_url = &path_mapper(app_path, conn_url);
97
98 if !Sqlite::database_exists(conn_url).await.unwrap_or(false) {
99 Sqlite::create_database(conn_url).await?;
100 }
101 Ok(Self::Sqlite(Pool::connect(conn_url).await?))
102 }
103 #[cfg(feature = "mysql")]
104 "mysql" => {
105 if !MySql::database_exists(conn_url).await.unwrap_or(false) {
106 MySql::create_database(conn_url).await?;
107 }
108 Ok(Self::MySql(Pool::connect(conn_url).await?))
109 }
110 #[cfg(feature = "postgres")]
111 "postgres" => {
112 if !Postgres::database_exists(conn_url).await.unwrap_or(false) {
113 Postgres::create_database(conn_url).await?;
114 }
115 Ok(Self::Postgres(Pool::connect(conn_url).await?))
116 }
117 #[cfg(not(any(feature = "sqlite", feature = "postgres", feature = "mysql")))]
118 _ => Err(crate::Error::InvalidDbUrl(format!(
119 "{conn_url} - No database driver enabled!"
120 ))),
121 #[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))]
122 _ => Err(crate::Error::InvalidDbUrl(conn_url.to_string())),
123 }
124 }
125
126 pub(crate) async fn migrate(
127 &self,
128 _migrator: &sqlx::migrate::Migrator,
129 ) -> Result<(), crate::Error> {
130 match self {
131 #[cfg(feature = "sqlite")]
132 DbPool::Sqlite(pool) => _migrator.run(pool).await?,
133 #[cfg(feature = "mysql")]
134 DbPool::MySql(pool) => _migrator.run(pool).await?,
135 #[cfg(feature = "postgres")]
136 DbPool::Postgres(pool) => _migrator.run(pool).await?,
137 #[cfg(not(any(feature = "sqlite", feature = "mysql", feature = "postgres")))]
138 DbPool::None => (),
139 }
140 Ok(())
141 }
142
143 pub(crate) async fn close(&self) {
144 match self {
145 #[cfg(feature = "sqlite")]
146 DbPool::Sqlite(pool) => pool.close().await,
147 #[cfg(feature = "mysql")]
148 DbPool::MySql(pool) => pool.close().await,
149 #[cfg(feature = "postgres")]
150 DbPool::Postgres(pool) => pool.close().await,
151 #[cfg(not(any(feature = "sqlite", feature = "mysql", feature = "postgres")))]
152 DbPool::None => (),
153 }
154 }
155
156 pub(crate) async fn execute(
157 &self,
158 _query: String,
159 _values: Vec<JsonValue>,
160 ) -> Result<(u64, LastInsertId), crate::Error> {
161 Ok(match self {
162 #[cfg(feature = "sqlite")]
163 DbPool::Sqlite(pool) => {
164 let mut query = sqlx::query(&_query);
165 for value in _values {
166 if value.is_null() {
167 query = query.bind(None::<JsonValue>);
168 } else if value.is_string() {
169 query = query.bind(value.as_str().unwrap().to_owned())
170 } else if let Some(number) = value.as_number() {
171 query = query.bind(number.as_f64().unwrap_or_default())
172 } else {
173 query = query.bind(value);
174 }
175 }
176 let result = pool.execute(query).await?;
177 (
178 result.rows_affected(),
179 LastInsertId::Sqlite(result.last_insert_rowid()),
180 )
181 }
182 #[cfg(feature = "mysql")]
183 DbPool::MySql(pool) => {
184 let mut query = sqlx::query(&_query);
185 for value in _values {
186 if value.is_null() {
187 query = query.bind(None::<JsonValue>);
188 } else if value.is_string() {
189 query = query.bind(value.as_str().unwrap().to_owned())
190 } else if let Some(number) = value.as_number() {
191 query = query.bind(number.as_f64().unwrap_or_default())
192 } else {
193 query = query.bind(value);
194 }
195 }
196 let result = pool.execute(query).await?;
197 (
198 result.rows_affected(),
199 LastInsertId::MySql(result.last_insert_id()),
200 )
201 }
202 #[cfg(feature = "postgres")]
203 DbPool::Postgres(pool) => {
204 let mut query = sqlx::query(&_query);
205 for value in _values {
206 if value.is_null() {
207 query = query.bind(None::<JsonValue>);
208 } else if value.is_string() {
209 query = query.bind(value.as_str().unwrap().to_owned())
210 } else if let Some(number) = value.as_number() {
211 query = query.bind(number.as_f64().unwrap_or_default())
212 } else {
213 query = query.bind(value);
214 }
215 }
216 let result = pool.execute(query).await?;
217 (result.rows_affected(), LastInsertId::Postgres(()))
218 }
219 #[cfg(not(any(feature = "sqlite", feature = "mysql", feature = "postgres")))]
220 DbPool::None => (0, LastInsertId::None),
221 })
222 }
223
224 pub(crate) async fn select(
225 &self,
226 _query: String,
227 _values: Vec<JsonValue>,
228 ) -> Result<Vec<IndexMap<String, JsonValue>>, crate::Error> {
229 Ok(match self {
230 #[cfg(feature = "sqlite")]
231 DbPool::Sqlite(pool) => {
232 let mut query = sqlx::query(&_query);
233 for value in _values {
234 if value.is_null() {
235 query = query.bind(None::<JsonValue>);
236 } else if value.is_string() {
237 query = query.bind(value.as_str().unwrap().to_owned())
238 } else if let Some(number) = value.as_number() {
239 query = query.bind(number.as_f64().unwrap_or_default())
240 } else {
241 query = query.bind(value);
242 }
243 }
244 let rows = pool.fetch_all(query).await?;
245 let mut values = Vec::new();
246 for row in rows {
247 let mut value = IndexMap::default();
248 for (i, column) in row.columns().iter().enumerate() {
249 let v = row.try_get_raw(i)?;
250
251 let v = crate::decode::sqlite::to_json(v)?;
252
253 value.insert(column.name().to_string(), v);
254 }
255
256 values.push(value);
257 }
258 values
259 }
260 #[cfg(feature = "mysql")]
261 DbPool::MySql(pool) => {
262 let mut query = sqlx::query(&_query);
263 for value in _values {
264 if value.is_null() {
265 query = query.bind(None::<JsonValue>);
266 } else if value.is_string() {
267 query = query.bind(value.as_str().unwrap().to_owned())
268 } else if let Some(number) = value.as_number() {
269 query = query.bind(number.as_f64().unwrap_or_default())
270 } else {
271 query = query.bind(value);
272 }
273 }
274 let rows = pool.fetch_all(query).await?;
275 let mut values = Vec::new();
276 for row in rows {
277 let mut value = IndexMap::default();
278 for (i, column) in row.columns().iter().enumerate() {
279 let v = row.try_get_raw(i)?;
280
281 let v = crate::decode::mysql::to_json(v)?;
282
283 value.insert(column.name().to_string(), v);
284 }
285
286 values.push(value);
287 }
288 values
289 }
290 #[cfg(feature = "postgres")]
291 DbPool::Postgres(pool) => {
292 let mut query = sqlx::query(&_query);
293 for value in _values {
294 if value.is_null() {
295 query = query.bind(None::<JsonValue>);
296 } else if value.is_string() {
297 query = query.bind(value.as_str().unwrap().to_owned())
298 } else if let Some(number) = value.as_number() {
299 query = query.bind(number.as_f64().unwrap_or_default())
300 } else {
301 query = query.bind(value);
302 }
303 }
304 let rows = pool.fetch_all(query).await?;
305 let mut values = Vec::new();
306 for row in rows {
307 let mut value = IndexMap::default();
308 for (i, column) in row.columns().iter().enumerate() {
309 let v = row.try_get_raw(i)?;
310
311 let v = crate::decode::postgres::to_json(v)?;
312
313 value.insert(column.name().to_string(), v);
314 }
315
316 values.push(value);
317 }
318 values
319 }
320 #[cfg(not(any(feature = "sqlite", feature = "mysql", feature = "postgres")))]
321 DbPool::None => Vec::new(),
322 })
323 }
324}
325
326#[cfg(feature = "sqlite")]
327fn path_mapper(mut app_path: std::path::PathBuf, connection_string: &str) -> String {
330 app_path.push(
331 connection_string
332 .split_once(':')
333 .expect("Couldn't parse the connection string for DB!")
334 .1,
335 );
336
337 format!(
338 "sqlite:{}",
339 app_path
340 .to_str()
341 .expect("Problem creating fully qualified path to Database file!")
342 )
343}