Skip to content

Commit 8e6fd80

Browse files
committed
feat: sqlite store initial implementation
1 parent d914044 commit 8e6fd80

6 files changed

Lines changed: 129 additions & 15 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ categories = ["command-line-utilities"]
1414

1515
[dependencies]
1616
json = "0.12.4"
17+
rusqlite = { version = "0.39.0", features = ["bundled"] }
1718
seahorse = "2.1.0"

src/actions.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@ use std::path::Path;
22

33
use seahorse::{ActionError, ActionResult, Context};
44

5-
use crate::config::get_config_path;
5+
use crate::config::{get_config_path, get_db_path};
66
use crate::error::invalid;
7-
use crate::storage::json::JSONStore;
8-
use crate::storage::{Store, StoreValue};
7+
use crate::storage::sqlite::SQLiteStore;
8+
use crate::storage::{self, Store, StoreValue};
99

1010
pub fn init_action(_c: &Context) -> ActionResult {
11-
let config_path = get_config_path();
11+
let config_path = get_db_path();
1212
let path = Path::new(&config_path);
1313

1414
if path.exists() {
1515
println!("config file already exists");
1616
}
1717

18-
JSONStore::with_force_create(true);
18+
SQLiteStore::from_path(path);
1919

2020
Ok(())
2121
}
2222

2323
pub fn list_action(c: &Context) -> ActionResult {
24-
let store = JSONStore::with_force_create(c.bool_flag("force-create"));
24+
let store = storage::load_storage();
2525

2626
for (key, value) in store.all().iter() {
2727
println!("{}\t{}", key, value);
@@ -31,7 +31,7 @@ pub fn list_action(c: &Context) -> ActionResult {
3131
}
3232

3333
pub fn clear_action(_c: &Context) -> ActionResult {
34-
let mut store = JSONStore::new();
34+
let mut store = storage::load_storage();
3535

3636
store.clear();
3737

@@ -51,7 +51,7 @@ pub fn get_action(c: &Context) -> ActionResult {
5151
return Err(invalid("key"));
5252
};
5353

54-
let store = JSONStore::new();
54+
let store = storage::load_storage();
5555

5656
let value = store.get(key);
5757

@@ -86,12 +86,12 @@ pub fn set_action(c: &Context) -> ActionResult {
8686
return Err(invalid("value"));
8787
};
8888

89-
let mut store = JSONStore::new();
89+
let mut store = storage::load_storage();
9090

9191
let value = StoreValue::Value(value_str.to_owned());
9292
store.set(key, value.clone());
9393

94-
println!("{}\t{}", key, value);
94+
println!("'{}' -> '{}'", key, value);
9595

9696
Ok(())
9797
}
@@ -101,7 +101,7 @@ pub fn remove_action(c: &Context) -> ActionResult {
101101
return Err(invalid("key"));
102102
};
103103

104-
let mut store = JSONStore::new();
104+
let mut store = storage::load_storage();
105105

106106
match store.remove(key) {
107107
Some(value) => println!("{}\t{}", key, value),

src/config.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
use std::{env::home_dir, path::PathBuf};
22

3+
pub fn get_config_folder_path() -> PathBuf {
4+
return home_dir().expect("couldn't find home directory");
5+
}
6+
37
pub fn get_config_path() -> PathBuf {
4-
let home_folder = home_dir().expect("couldn't find home directory");
5-
let path = home_folder.join(".cfs.json");
8+
return get_config_folder_path().join(".cfs.json");
9+
}
610

7-
return path;
11+
pub fn get_db_path() -> PathBuf {
12+
return get_config_folder_path().join(".cfs.db");
813
}

src/storage/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
pub mod json;
2-
mod sqlite;
2+
pub mod sqlite;
33
mod value;
44

55
pub use value::StoreValue;
66

7+
use crate::config::get_db_path;
8+
79
pub trait Store {
810
fn all(&self) -> Vec<(String, StoreValue)>;
911

@@ -13,3 +15,8 @@ pub trait Store {
1315

1416
fn clear(&mut self);
1517
}
18+
19+
//TODO: Change STORE Based on config.
20+
pub fn load_storage() -> impl Store {
21+
return sqlite::SQLiteStore::from_path(get_db_path());
22+
}

src/storage/sqlite/mod.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use std::path::Path;
2+
3+
use rusqlite::OptionalExtension as _;
4+
5+
use crate::storage::{Store, StoreValue};
6+
7+
#[derive(Debug)]
8+
pub struct SQLiteStore {
9+
connection: rusqlite::Connection,
10+
}
11+
12+
impl SQLiteStore {
13+
pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
14+
let conn = rusqlite::Connection::open(path).expect("To Open SQLite DB");
15+
16+
conn
17+
.execute_batch(include_str!("schema.sql"))
18+
.expect("To Create DB");
19+
20+
return Self { connection: conn };
21+
}
22+
}
23+
24+
impl Store for SQLiteStore {
25+
fn all(&self) -> Vec<(String, super::StoreValue)> {
26+
let mut stmt = self.connection.prepare("SELECT key,value from KV").unwrap();
27+
28+
let values = stmt
29+
.query_map([], |row| {
30+
let key: String = row.get(0)?;
31+
let value = StoreValue::Value(row.get::<_, String>(1)?);
32+
33+
return Ok((key, value));
34+
})
35+
.unwrap()
36+
.collect::<Result<Vec<_>, _>>()
37+
.unwrap();
38+
39+
return values;
40+
}
41+
42+
fn get(&self, key: &str) -> Option<super::StoreValue> {
43+
let stmt = self
44+
.connection
45+
.query_row(
46+
"SELECT key,value from KV where key = ?1 LIMIT 1",
47+
[key],
48+
|row| Ok(StoreValue::Value(row.get(1).unwrap())),
49+
)
50+
.optional()
51+
.unwrap();
52+
53+
return stmt;
54+
}
55+
56+
fn set(&mut self, key: &str, value: super::StoreValue) -> super::StoreValue {
57+
let StoreValue::Value(value) = value else {
58+
panic!("Invalid value passed into SQLiteStore GET [{}]", value)
59+
};
60+
61+
self
62+
.connection
63+
.execute(
64+
"INSERT INTO KV VALUES(NULL,?1,?2) ON CONFLICT(key) DO UPDATE SET value = ?2 WHERE key = ?1",
65+
[key, &value],
66+
)
67+
.unwrap();
68+
69+
return StoreValue::Value(value);
70+
}
71+
72+
fn remove(&mut self, key: &str) -> Option<super::StoreValue> {
73+
let value = self.get(key);
74+
75+
let Some(value) = value else {
76+
return None;
77+
};
78+
79+
let stmt = self
80+
.connection
81+
.execute("DELETE FROM KV where key = ?1", [key])
82+
.unwrap();
83+
84+
if stmt == 0 {
85+
panic!("Deleted 0 Rows when trying to delete Value from Store")
86+
}
87+
88+
Some(value)
89+
}
90+
91+
fn clear(&mut self) {
92+
self.connection.execute("DELETE FROM KV", []).unwrap();
93+
}
94+
}

src/storage/sqlite/schema.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE IF NOT EXISTS KV (
2+
id INTEGER PRIMARY KEY,
3+
key TEXT NOT NULL,
4+
value TEXT NOT NULL
5+
);
6+
7+
CREATE UNIQUE INDEX IF NOT EXISTS kv_keys ON KV (key);

0 commit comments

Comments
 (0)