use chrono::offset::Utc; use rusqlite::{types::ToSqlOutput, ToSql}; use tauri::{Manager, State}; use tauri_plugin_fs::FsExt; use tokio::sync::Mutex; use std::collections::HashMap; mod app_state; mod data_door; use app_state::AppState; #[derive(Clone, Copy, Debug)] enum Store { Aldi, Edeka, Dm, Lidl, Rewe, Tegut, } #[derive(Clone, Copy, Debug)] enum Account { Sumpf, Heinersyndikat, } struct Inventory { acc: Account, cash: i64, vouchers: Vec, } #[derive(Debug)] struct VoucherInventory { store: Store, count: i64, } impl TryFrom<&str> for Store { type Error = (); fn try_from(s: &str) -> Result { match s { "aldi" => Ok(Store::Aldi), "edeka" => Ok(Store::Edeka), "dm" => Ok(Store::Dm), "lidl" => Ok(Store::Lidl), "rewe" => Ok(Store::Rewe), "tegut" => Ok(Store::Tegut), _ => Err(()), } } } impl TryFrom<&str> for Account { type Error = (); fn try_from(s: &str) -> Result { match s { "sumpf" => Ok(Account::Sumpf), "hs" => Ok(Account::Heinersyndikat), _ => Err(()), } } } impl ToSql for Store { fn to_sql(&self) -> rusqlite::Result> { match self { Store::Aldi => 0.to_sql(), Store::Edeka => 1.to_sql(), Store::Dm => 2.to_sql(), Store::Lidl => 3.to_sql(), Store::Rewe => 4.to_sql(), Store::Tegut => 5.to_sql(), } } } impl ToSql for Account { fn to_sql(&self) -> rusqlite::Result> { match self { Account::Sumpf => 0.to_sql(), Account::Heinersyndikat => 1.to_sql(), } } } fn parse_inventory(data: HashMap) -> Result { let a = data.get("cafe-inventory-acc").ok_or(())?; let acc: Account = Account::try_from(a.as_ref())?; let mut vouchers = Vec::new(); for s in ["aldi", "dm", "lidl", "rewe", "tegut"] { let Ok(store) = s.try_into() else { println!("Did not find '{}' in inventory data.", s); continue; }; match data.get(&format!("cafe-inventory-{}", s)) { None => (), Some(c) => { let c = if c == "" {"0"} else {c}; let Ok(count) = c.parse() else { println!("Invalid count '{}' for '{}' in inventory data.", c, s); continue; }; let v = VoucherInventory { store, count }; vouchers.push(v); }, } } unimplemented!() } #[tauri::command] async fn inventory( data: HashMap, state: State<'_, Mutex>, ) -> Result<(), ()> { let now = Utc::now().timestamp(); let 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, ), ) .map_err(|e| println!("{:?}", e))?; }; Ok(()) } #[tauri::command] async fn swap( store: &str, acc: i64, state: State<'_, Mutex>, ) -> Result<(), ()> { let state = state.lock().await; let store: Store = store.try_into()?; 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, ), ) .map_err(|e| println!("{:?}", e))?; Ok(()) } #[tauri::command] async fn count(state: State<'_, Mutex>) -> Result { let state = state.lock().await; let mut stmt = state.db.prepare("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()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let state = AppState::new(); tauri::Builder::default() .plugin(tauri_plugin_fs::init()) .setup(|app| { app.manage(Mutex::new(state)); let scope = app.fs_scope(); let path = app.path(); scope.allow_directory(path.temp_dir()?, false)?; Ok(()) }) .invoke_handler(tauri::generate_handler![ swap, count, inventory, data_door::pull_data, data_door::push_data, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }