diff --git a/crates/store/Cargo.lock b/crates/store/Cargo.lock index 5d2508f..0c174da 100644 --- a/crates/store/Cargo.lock +++ b/crates/store/Cargo.lock @@ -67,7 +67,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -150,9 +150,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" @@ -171,9 +171,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.27" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56b4c72906975ca04becb8a30e102dfecddd0c06181e3e95ddc444be28881f8" +checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" dependencies = [ "android-tzdata", "iana-time-zone", @@ -340,6 +340,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + [[package]] name = "flume" version = "0.10.14" @@ -572,9 +578,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] @@ -659,9 +665,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "minimal-lexical" @@ -759,9 +765,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -833,7 +839,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -973,7 +979,6 @@ dependencies = [ "anyhow", "async-trait", "serde", - "serde_json", "sha2", "sqlx", "tokio", @@ -982,9 +987,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.10" +version = "0.38.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964" +checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" dependencies = [ "bitflags 2.4.0", "errno", @@ -1022,7 +1027,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1138,9 +1143,9 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85" dependencies = [ "itertools", "nom", @@ -1356,10 +1361,11 @@ dependencies = [ [[package]] name = "stringprep" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" dependencies = [ + "finl_unicode", "unicode-bidi", "unicode-normalization", ] @@ -1383,9 +1389,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" dependencies = [ "proc-macro2", "quote", @@ -1407,22 +1413,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1495,7 +1501,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1511,9 +1517,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", "serde_spanned", @@ -1532,9 +1538,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "serde", @@ -1564,7 +1570,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", ] [[package]] @@ -1671,7 +1677,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", "wasm-bindgen-shared", ] @@ -1693,7 +1699,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.31", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/crates/store/src/calendar.rs b/crates/store/src/calendar.rs index caded1a..d4c1cd6 100644 --- a/crates/store/src/calendar.rs +++ b/crates/store/src/calendar.rs @@ -1,10 +1,7 @@ -use std::collections::HashMap; - -use anyhow::{anyhow, Result}; +use anyhow::Result; use async_trait::async_trait; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; -use tokio::{fs::File, io::AsyncWriteExt}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Event { @@ -13,6 +10,9 @@ pub struct Event { } impl Event { + pub fn from_ics(uid: String, ics: String) -> Self { + Self { uid, ics } + } pub fn get_uid(&self) -> &str { &self.uid } @@ -28,14 +28,14 @@ impl Event { } } -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, Deserialize, Serialize)] pub struct Calendar { pub id: String, pub name: Option, pub owner: String, pub description: Option, pub color: Option, - pub ics: String, + pub timezone: Option, } impl Calendar {} @@ -44,84 +44,10 @@ impl Calendar {} pub trait CalendarStore: Send + Sync + 'static { async fn get_calendar(&self, id: &str) -> Result; async fn get_calendars(&self, owner: &str) -> Result>; + async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<()>; async fn get_events(&self, cid: &str) -> Result>; async fn get_event(&self, cid: &str, uid: &str) -> Result; async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<()>; async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<()>; } - -#[derive(Debug, Deserialize, Serialize)] -pub struct TomlCalendarStore { - calendars: HashMap, - events: HashMap>, - path: String, -} - -impl TomlCalendarStore { - pub fn new(path: String) -> Self { - TomlCalendarStore { - calendars: HashMap::new(), - events: HashMap::new(), - path, - } - } - - pub async fn save(&self) -> Result<()> { - let mut file = File::create(&self.path).await?; - let output = toml::to_string_pretty(&self)?; - file.write_all(output.as_bytes()).await?; - Ok(()) - } -} - -#[async_trait] -impl CalendarStore for TomlCalendarStore { - async fn get_calendar(&self, id: &str) -> Result { - Ok(self.calendars.get(id).ok_or(anyhow!("not found"))?.clone()) - } - - async fn get_calendars(&self, user: &str) -> Result> { - Ok(self - .calendars - .values() - .filter(|Calendar { owner, .. }| owner == user) - .cloned() - .collect()) - } - - async fn get_events(&self, cid: &str) -> Result> { - if let Some(events) = self.events.get(cid) { - Ok(events.values().cloned().collect()) - } else { - Ok(Vec::new()) - } - } - - async fn get_event(&self, cid: &str, uid: &str) -> Result { - let events = self.events.get(cid).ok_or(anyhow!("not found"))?; - Ok(events.get(uid).ok_or(anyhow!("not found"))?.clone()) - } - - async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<()> { - let events = self.events.entry(cid).or_insert(HashMap::new()); - events.insert( - uid.clone(), - Event { - uid, - ics, - summary: None, - }, - ); - self.save().await.unwrap(); - Ok(()) - } - - async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<()> { - if let Some(events) = self.events.get_mut(cid) { - events.remove(uid); - self.save().await?; - } - Ok(()) - } -} diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index 7c035ea..be4740b 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -1,2 +1,3 @@ pub mod calendar; pub mod models; +pub mod toml_store; diff --git a/crates/store/src/toml_store.rs b/crates/store/src/toml_store.rs new file mode 100644 index 0000000..176af50 --- /dev/null +++ b/crates/store/src/toml_store.rs @@ -0,0 +1,85 @@ +use crate::calendar::{Calendar, CalendarStore, Event}; +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; +use std::collections::{hash_map::Entry, HashMap}; +use tokio::{fs::File, io::AsyncWriteExt}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct TomlCalendarStore { + calendars: HashMap, + events: HashMap>, + path: String, +} + +impl TomlCalendarStore { + pub fn new(path: String) -> Self { + TomlCalendarStore { + calendars: HashMap::new(), + events: HashMap::new(), + path, + } + } + + pub async fn save(&self) -> Result<()> { + let mut file = File::create(&self.path).await?; + let output = toml::to_string_pretty(&self)?; + file.write_all(output.as_bytes()).await?; + Ok(()) + } +} + +#[async_trait] +impl CalendarStore for TomlCalendarStore { + async fn get_calendar(&self, id: &str) -> Result { + Ok(self.calendars.get(id).ok_or(anyhow!("not found"))?.clone()) + } + + async fn get_calendars(&self, user: &str) -> Result> { + Ok(self + .calendars + .values() + .filter(|Calendar { owner, .. }| owner == user) + .cloned() + .collect()) + } + + async fn insert_calendar(&mut self, cid: String, calendar: Calendar) -> Result<()> { + match self.calendars.entry(cid) { + Entry::Occupied(_) => Err(anyhow!("calendar already exists")), + Entry::Vacant(v) => { + v.insert(calendar); + self.save().await.unwrap(); + Ok(()) + } + } + } + + async fn get_events(&self, cid: &str) -> Result> { + if let Some(events) = self.events.get(cid) { + Ok(events.values().cloned().collect()) + } else { + Ok(Vec::new()) + } + } + + async fn get_event(&self, cid: &str, uid: &str) -> Result { + let events = self.events.get(cid).ok_or(anyhow!("not found"))?; + Ok(events.get(uid).ok_or(anyhow!("not found"))?.clone()) + } + + async fn upsert_event(&mut self, cid: String, uid: String, ics: String) -> Result<()> { + let events = self.events.entry(cid).or_insert(HashMap::new()); + events.insert(uid.clone(), Event::from_ics(uid, ics)); + self.save().await.unwrap(); + Ok(()) + } + + async fn delete_event(&mut self, cid: &str, uid: &str) -> Result<()> { + if let Some(events) = self.events.get_mut(cid) { + events.remove(uid); + self.save().await?; + } + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 1c3f9aa..0c97429 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use rustical_api::configure_api; use rustical_auth::AuthProvider; use rustical_dav::{configure_dav, configure_well_known}; use rustical_frontend::configure_frontend; -use rustical_store::calendar::TomlCalendarStore; +use rustical_store::toml_store::TomlCalendarStore; use std::fs; use std::sync::Arc; use tokio::sync::RwLock;