diff --git a/Cargo.toml b/Cargo.toml index 1699701..2260939 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ ring-compat = { version = "^0.8", features = [ "signature", "rand_core", ], optional = true } -sqlx = { version = "^0.8", features = ["sqlite"], optional = true } +sqlx = { version = "^0.8", features = ["sqlite", "runtime-tokio"], optional = true } tauri = { version = "2", features = ["config-toml"], optional = true } tauri-plugin-fs = { version = "2", optional = true } tauri-plugin-opener = { version = "2", optional = true } diff --git a/migrations/0000_init.sql b/migrations/0000_init.sql new file mode 100644 index 0000000..1aa1325 --- /dev/null +++ b/migrations/0000_init.sql @@ -0,0 +1,29 @@ +CREATE TABLE `swap` ( + `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `acc` INTEGER NOT NULL, + `voucher` INTEGER NOT NULL, + `storno` INTEGER NOT NULL, + `timestamp` INTEGER NOT NULL +); + +CREATE TABLE `voucher_type` ( + `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `store` INTEGER NOT NULL, + `value` INTEGER NOT NULL, + UNIQUE (`store`, `value`) +); + +CREATE TABLE `inventory` ( + `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + `acc` INTEGER NOT NULL, + `cash` INTEGER NOT NULL, + `timestamp` INTEGER NOT NULL, + UNIQUE (`acc`, `timestamp`) +); + +CREATE TABLE `voucher_inventory` ( + `inventory` INTEGER NOT NULL, + `voucher` INTEGER NOT NULL, + `count` INTEGER NOT NULL, + PRIMARY KEY (`inventory`, `voucher`) +); diff --git a/src/lib.rs b/src/lib.rs index ef98862..32bb1c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,6 @@ pub fn run() { }) .invoke_handler(tauri::generate_handler![ server::swap, - server::count, server::inventory, server::data_door::pull_data, server::data_door::push_data, diff --git a/src/server/app_state.rs b/src/server/app_state.rs index a67f163..0814efa 100644 --- a/src/server/app_state.rs +++ b/src/server/app_state.rs @@ -12,7 +12,7 @@ pub struct AppState { impl AppState { pub fn new() -> Self { - todo!() + todo!(); let db = unimplemented!(); let mut rng = rand::thread_rng(); let last_sync = i64::MIN; diff --git a/src/server/mod.rs b/src/server/mod.rs index 715c3f3..f852268 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -11,6 +11,10 @@ pub mod data_door; use app_state::AppState; +struct Id { + id: i64, +} + fn parse_inventory(data: HashMap) -> Result { let a = data.get("cafe-inventory-acc").ok_or(())?; let acc: Account = Account::try_from(a.as_ref())?; @@ -24,11 +28,13 @@ fn parse_inventory(data: HashMap) -> Result { None => (), Some(c) => { let c = if c == "" { "0" } else { c }; - let Ok(count) = c.parse() else { + let Ok(count): Result = c.parse() else { println!("Invalid count '{}' for '{}' in inventory data.", c, s); continue; }; - let v = VoucherInventory { store, count }; + let value = 50_00; + let voucher = VoucherType { store, value }; + let v = (voucher, count); vouchers.push(v); } } @@ -36,6 +42,28 @@ fn parse_inventory(data: HashMap) -> Result { unimplemented!() } +pub async fn voucher_id(v: VoucherType, db: &mut Connection) -> Result { + sqlx::query!( + "INSERT OR IGNORE INTO voucher_type(store, value) VALUES (?1, ?2)", + v.store, + v.value, + ) + .execute(&mut *db) + .await + .map_err(|e| println!("{:?}", e))?; + let id = sqlx::query_as!( + Id, + "SELECT id FROM voucher_type WHERE store = ?1 AND value = ?2", + v.store, + v.value, + ) + .fetch_one(db) + .await + .map_err(|e| println!("{:?}", e))? + .id; + Ok(id) +} + #[tauri::command] pub async fn inventory( data: HashMap, @@ -43,13 +71,37 @@ pub async fn inventory( ) -> Result<(), ()> { println!("{:?}", data); let now = Utc::now().timestamp(); - let state = state.lock().await; + let mut state = state.lock().await; let inv = parse_inventory(data)?; - for v in inv.vouchers { - state.db.execute( - "INSERT INTO voucher_inventory VALUES ()", - (inv.acc, v.store, v.count, now), + sqlx::query!( + "INSERT INTO inventory(acc, cash, timestamp) VALUES (?1, ?2, ?3)", + inv.acc, + inv.cash, + now, + ) + .execute(&mut state.db) + .await + .map_err(|e| println!("{:?}", e))?; + let inventory = sqlx::query_as!( + Id, + "SELECT id FROM inventory WHERE acc = ?1 AND timestamp = ?2", + inv.acc, + now, + ) + .fetch_one(&mut state.db) + .await + .map_err(|e| println!("{:?}", e))? + .id; + for (v, count) in inv.vouchers { + let voucher = voucher_id(v, &mut state.db).await?; + sqlx::query!( + "INSERT INTO voucher_inventory VALUES (?1, ?2, ?3)", + inventory, + voucher, + count, ) + .execute(&mut state.db) + .await .map_err(|e| println!("{:?}", e))?; } Ok(()) @@ -61,36 +113,21 @@ pub async fn swap( acc: i64, state: State<'_, Mutex>, ) -> Result<(), ()> { - let state = state.lock().await; - state.db.execute( - "INSERT INTO swap VALUES (?1, ?2, ?3, ?4, ?5)", - ( - store, - acc, - i64::from_ne_bytes(state.id.to_ne_bytes()), - Utc::now().timestamp(), - false, - ), + let timestamp = Utc::now().timestamp(); + let mut state = state.lock().await; + let id = i64::from_ne_bytes(state.id.to_ne_bytes()); + let value = 50_00; + let voucher = VoucherType{ store, value }; + let voucher_type = voucher_id(voucher, &mut state.db).await?; + sqlx::query!( + "INSERT INTO swap(acc, voucher, storno, timestamp) VALUES (?1, ?2, ?3, ?4)", + acc, + voucher_type, + false, + timestamp, ) + .execute(&mut state.db) + .await .map_err(|e| println!("{:?}", e))?; Ok(()) } - -#[tauri::command] -pub async fn count(state: State<'_, Mutex>) -> Result { - let state = state.lock().await; - let mut stmt = - state.db.prepare_with("SELECT COUNT(*) FROM swap") - .map_err(|e| println!("{:?}", e))?; - let mut rows = stmt.query([]).map_err(|e| println!("{:?}", e))?; - let row = rows.next().map_err(|e| println!("{:?}", e))?; - let row = match row { - Some(r) => Ok(r), - None => { - println!("No rows"); - Err(()) - } - }?; - let cnt: u64 = row.get(0).map_err(|e| println!("{:?}", e))?; - Ok(cnt.to_string()) -} diff --git a/src/types/account.rs b/src/types/account.rs index 8b89bb9..e5f3cf7 100644 --- a/src/types/account.rs +++ b/src/types/account.rs @@ -1,10 +1,11 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] -#[cfg_attr(features = "dep:sqlx", derive(sqlx::Type))] +#[cfg_attr(feature = "server", derive(sqlx::Type))] +#[cfg_attr(feature = "server", repr(i64))] pub enum Account { - Sumpf, - Heinersyndikat, + Sumpf = 1, + Heinersyndikat = 2, } impl TryFrom<&str> for Account { @@ -36,4 +37,4 @@ impl std::fmt::Display for Account { } .fmt(f) } -} \ No newline at end of file +} diff --git a/src/types/cash.rs b/src/types/cash.rs index d73255e..870b953 100644 --- a/src/types/cash.rs +++ b/src/types/cash.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; /// measured as an integer multiple /// of 0.01 €. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "server", derive(sqlx::Type))] +#[cfg_attr(feature = "server", sqlx(transparent))] pub struct Cash(i64); impl std::str::FromStr for Cash { @@ -20,4 +22,4 @@ impl std::str::FromStr for Cash { }; Ok(Cash(i * 100 + f)) } -} \ No newline at end of file +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 0fa5794..eaf0734 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -8,15 +8,15 @@ pub use store::Store; pub use account::Account; pub use cash::Cash; +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct VoucherType { + pub store: Store, + pub value: i64, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Inventory { pub acc: Account, pub cash: Cash, - pub vouchers: Vec, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct VoucherInventory { - pub store: Store, - pub count: i64, + pub vouchers: Vec<(VoucherType, i64)>, } diff --git a/src/types/store.rs b/src/types/store.rs index 2531000..841ff94 100644 --- a/src/types/store.rs +++ b/src/types/store.rs @@ -2,13 +2,14 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[cfg_attr(feature = "server", derive(sqlx::Type))] +#[cfg_attr(feature = "server", repr(i64))] pub enum Store { - Aldi, - Edeka, - Dm, - Lidl, - Rewe, - Tegut, + Aldi = 1, + Edeka = 2, + Dm = 3, + Lidl = 4, + Rewe = 5, + Tegut = 6, } impl Into for &Store {