1use libsql::{params, params_from_iter};
17use libsql::{Builder, Connection};
18use serde_json::Value;
19use std::future::Future;
20use std::path::PathBuf;
21use std::pin::Pin;
22use std::sync::Arc;
23use std::time::Instant;
24
25use crate::{Store, StoreError, StoreModel, DEFAULT_NAMESPACE_NAME};
26
27pub struct KyvalStoreBuilder {
77 uri: Option<PathBuf>,
78 token: Option<String>,
79 connnection: Option<Arc<Connection>>,
80 table_name: Option<String>,
81}
82
83impl KyvalStoreBuilder {
84 pub fn new() -> Self {
85 Self {
86 uri: None,
87 token: None,
88 connnection: None,
89 table_name: None,
90 }
91 }
92
93 pub fn table_name<S: Into<String>>(mut self, table: S) -> Self {
98 self.table_name = Some(table.into());
99 self
100 }
101
102 pub fn uri<S: Into<PathBuf>>(mut self, uri: S) -> Self {
106 self.uri = Some(uri.into());
107 self
108 }
109
110 pub fn token<S: Into<String>>(mut self, token: S) -> Self {
114 self.token = Some(token.into());
115 self
116 }
117
118 pub fn connnection(mut self, connnection: Arc<Connection>) -> Self {
123 self.connnection = Some(connnection);
124 self
125 }
126
127 pub async fn build(self) -> Result<KyvalStore, StoreError> {
136 let connnection = match self.connnection {
137 Some(connnection) => connnection,
138 None => {
139 let path = self
140 .uri
141 .expect("KyvalStore requires either a URI or an existing connnection to be set");
142
143 let db = if let Some(token) = self.token {
145 Builder::new_remote(path.display().to_string(), token)
146 .build()
147 .await
148 .map_err(|_| {
149 StoreError::ConnectionError(
150 "Failed to create database connection"
151 .to_string(),
152 )
153 })?
154 } else {
155 Builder::new_local(path).build().await.map_err(|_| {
156 StoreError::ConnectionError(
157 "Failed to create database connection".to_string(),
158 )
159 })?
160 };
161
162 let conn = db.connect().map_err(|_| {
163 StoreError::ConnectionError(
164 "Failed to create database connnection".to_string(),
165 )
166 })?;
167
168 Arc::new(conn)
169 }
170 };
171
172 let table_name = self.table_name.unwrap_or_else(|| {
173 log::warn!("Table name not set, using default table name");
174 DEFAULT_NAMESPACE_NAME.to_string()
175 });
176
177 Ok(KyvalStore {
178 connnection,
179 table_name,
180 })
181 }
182}
183
184pub struct KyvalStore {
185 pub(crate) connnection: Arc<Connection>,
186 pub(crate) table_name: String,
187}
188
189impl KyvalStore {
190 fn get_table_name(&self) -> String {
191 self.table_name.clone()
192 }
193}
194
195impl Store for KyvalStore {
196 fn initialize(
197 &self,
198 ) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + '_>> {
199 let query = format!(
200 r#"
201 CREATE TABLE IF NOT EXISTS {table_name} (
202 key TEXT PRIMARY KEY,
203 value TEXT NOT NULL,
204 updated_at TEXT DEFAULT (datetime('now', 'localtime')),
205 UNIQUE(key)
206 ) STRICT;
207 CREATE INDEX IF NOT EXISTS {table_name}_key_idx ON {table_name} (key);
208 CREATE TRIGGER IF NOT EXISTS {table_name}_update_trigger
209 AFTER UPDATE ON {table_name}
210 BEGIN
211 UPDATE {table_name} SET updated_at = datetime('now', 'localtime') WHERE key = NEW.key;
212 END;
213 "#,
214 table_name = self.get_table_name()
215 );
216
217 let conn = &*self.connnection;
218
219 Box::pin(async move {
220 conn.execute_batch(&query).await.map_err(|e| {
221 StoreError::QueryError(format!(
222 "Failed to initialize the database table: {}",
223 e
224 ))
225 })?;
226
227 Ok(())
228 })
229 }
230
231 fn get(
232 &self,
233 key: &str,
234 ) -> Pin<
235 Box<dyn Future<Output = Result<Option<Value>, StoreError>> + Send + '_>,
236 > {
237 let query = format!(
238 "SELECT value FROM {} WHERE key = ?1 LIMIT 1",
239 self.get_table_name()
240 );
241
242 let conn = &*self.connnection;
243 let key = key.to_string();
244
245 Box::pin(async move {
246 let start = Instant::now();
247
248 let mut stmt = conn.prepare(&query).await.map_err(|e| {
249 StoreError::QueryError(format!(
250 "Failed to set the statement: {:?}",
251 e
252 ))
253 })?;
254
255 let result =
256 stmt.query_row(params![key.clone()]).await.map_err(|e| {
257 StoreError::QueryError(format!(
258 "Failed to fetch the value: {:?}",
259 e
260 ))
261 })?;
262
263 let row_value: String = result.get(0).map_err(|e| {
264 StoreError::QueryError(format!(
265 "Failed to get the value: {:?}",
266 e
267 ))
268 })?;
269
270 let value = serde_json::to_value(row_value)
271 .map_err(|e| StoreError::SerializationError { source: e })?;
272
273 let duration = start.elapsed();
274 log::debug!(
275 "Kyval store get: {:?} | {} | {:?}",
276 duration,
277 key,
278 value
279 );
280
281 Ok(Some(value))
282 })
283 }
284
285 fn list(
286 &self,
287 ) -> Pin<
288 Box<
289 dyn Future<Output = Result<Vec<StoreModel>, StoreError>>
290 + Send
291 + '_,
292 >,
293 > {
294 let query = format!(
295 "SELECT key, value FROM {} ORDER BY key ASC;",
296 self.get_table_name()
297 );
298
299 let conn = &*self.connnection;
300
301 Box::pin(async move {
302 let start = Instant::now();
303
304 let mut stmt = conn.prepare(&query).await.map_err(|e| {
305 StoreError::QueryError(format!(
306 "Failed to set the statement: {:?}",
307 e
308 ))
309 })?;
310
311 let mut results = stmt.query(params![]).await.map_err(|e| {
312 StoreError::QueryError(format!(
313 "Failed to fetch the value: {:?}",
314 e
315 ))
316 })?;
317
318 let mut items: Vec<StoreModel> = Vec::new();
319
320 while let Some(row) = results.next().await.map_err(|e| {
321 StoreError::QueryError(format!(
322 "Failed to iterate rows: {:?}",
323 e
324 ))
325 })? {
326 let key: String = row.get(0).map_err(|e| {
327 StoreError::QueryError(format!(
328 "Failed to get the value: {:?}",
329 e
330 ))
331 })?;
332 let row_value: String = row.get(1).map_err(|e| {
333 StoreError::QueryError(format!(
334 "Failed to get the value: {:?}",
335 e
336 ))
337 })?;
338 let value = serde_json::to_value(row_value).map_err(|e| {
339 StoreError::SerializationError { source: e }
340 })?;
341
342 items.push(StoreModel { key, value });
343 }
344
345 let duration = start.elapsed();
346 log::debug!("Kyval store list: {:?} | {:?}", duration, items);
347
348 Ok(items)
349 })
350 }
351
352 fn set(
353 &self,
354 key: &str,
355 value: Value,
356 _ttl: Option<u64>,
357 ) -> Pin<
358 Box<
359 dyn Future<Output = Result<Option<StoreModel>, StoreError>>
360 + Send
361 + '_,
362 >,
363 > {
364 let query = format!(
365 "INSERT INTO {} (key, value) VALUES (?1, ?2) ON CONFLICT(key) DO UPDATE SET value = EXCLUDED.value",
366 self.get_table_name()
367 );
368
369 let conn = &*self.connnection;
370 let key = key.to_string();
371
372 Box::pin(async move {
373 let start = Instant::now();
374
375 let value_str = match value {
376 Value::String(ref s) => s.clone(), Value::Number(ref n) => n.to_string(), Value::Null => "".to_string(), _ => value.to_string(), };
381
382 let mut stmt = conn.prepare(&query).await.map_err(|_| {
383 StoreError::QueryError(
384 "Failed to set the statement".to_string(),
385 )
386 })?;
387
388 let mut response = stmt
389 .query(params![key.clone(), value_str.clone()])
390 .await
391 .map_err(|_| {
392 StoreError::QueryError(
393 "Failed to set the value".to_string(),
394 )
395 })?;
396
397 let result = match response.next().await.map_err(|e| {
398 StoreError::QueryError(format!(
399 "Failed to iterate rows: {:?}",
400 e
401 ))
402 })? {
403 Some(row) => {
404 let row_key = row.get_str(0).map_err(|e| {
405 StoreError::QueryError(format!(
406 "Failed to get the key: {:?}",
407 e
408 ))
409 })?;
410
411 let row_value = row.get_value(1).map_err(|e| {
412 StoreError::QueryError(format!(
413 "Failed to get the value: {:?}",
414 e
415 ))
416 })?;
417
418 Some(StoreModel {
419 key: row_key.to_string(),
420 value: serde_json::to_value(row_value).map_err(
421 |e| StoreError::SerializationError { source: e },
422 )?,
423 })
424 }
425 None => None,
426 };
427
428 let duration = start.elapsed();
429 log::debug!(
430 "Kyval store set: {:?} | {} | {}",
431 duration,
432 key,
433 value_str
434 );
435
436 Ok(result)
437 })
438 }
439
440 fn remove(
441 &self,
442 key: &str,
443 ) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + '_>> {
444 let query =
445 format!("DELETE FROM {} WHERE key = ?1", self.get_table_name());
446
447 let conn = &*self.connnection;
448
449 let key = key.to_string();
450
451 Box::pin(async move {
452 let start = Instant::now();
453
454 let mut stmt = conn.prepare(&query).await.map_err(|_| {
455 StoreError::QueryError(
456 "Failed to set the statement".to_string(),
457 )
458 })?;
459
460 stmt.execute(params![key.clone()]).await.map_err(|_| {
461 StoreError::QueryError("Failed to remove the key".to_string())
462 })?;
463
464 let duration = start.elapsed();
465 log::debug!("Kyval store remove: {:?} | {}", duration, key);
466
467 Ok(())
468 })
469 }
470
471 fn remove_many(
472 &self,
473 keys: &[&str],
474 ) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + '_>> {
475 let conn = &*self.connnection;
476
477 let placeholder = keys
478 .iter()
479 .enumerate()
480 .map(|(i, _)| format!("?{}", i + 1))
481 .collect::<Vec<String>>()
482 .join(", ");
483
484 let query = format!(
485 "DELETE FROM {} WHERE key IN ({})",
486 self.get_table_name(),
487 placeholder
488 );
489
490 let keys = keys.iter().map(|k| k.to_string()).collect::<Vec<String>>();
491
492 Box::pin(async move {
493 let start = Instant::now();
494
495 let mut stmt = conn.prepare(&query).await.map_err(|_| {
496 StoreError::QueryError(
497 "Failed to set the statement".to_string(),
498 )
499 })?;
500
501 stmt.execute(params_from_iter(keys)).await.map_err(|_| {
502 StoreError::QueryError("Failed to remove the key".to_string())
503 })?;
504
505 let duration = start.elapsed();
506 log::debug!("Kyval store remove_many: {:?}", duration);
507
508 Ok(())
509 })
510 }
511
512 fn clear(
513 &self,
514 ) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + '_>> {
515 let query = format!("DELETE FROM {}", self.get_table_name());
516
517 let conn = &*self.connnection;
518
519 Box::pin(async move {
520 conn.execute(&query, params![]).await.map_err(|_| {
521 StoreError::QueryError("Failed to clear the table".to_string())
522 })?;
523
524 Ok(())
525 })
526 }
527}