diff --git a/Cargo.lock b/Cargo.lock index db613a9..d94571a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "getrandom" version = "0.2.10" @@ -61,12 +67,40 @@ dependencies = [ "wasi", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "once_cell" version = "1.18.0" @@ -89,6 +123,8 @@ dependencies = [ "once_cell", "redb", "serde", + "serde_yaml", + "toml", "uuid", ] @@ -186,6 +222,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + [[package]] name = "serde" version = "1.0.171" @@ -206,6 +248,28 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5f51e3fdb5b9cdd1577e1cb7a733474191b1aca6a72c2e50913241632c1180" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "syn" version = "2.0.26" @@ -243,12 +307,52 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + [[package]] name = "uuid" version = "1.4.1" @@ -330,3 +434,12 @@ name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 0f5c1d8..4f6ee94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,6 @@ dirs = "5.0.1" once_cell = "1.18.0" redb = "1.0.5" serde = { version = "1.0.171", features = ["derive"] } +serde_yaml = "0.9.24" +toml = "0.7.6" uuid = { version = "1.4.1", features = ["v4", "v8", "fast-rng"] } diff --git a/apps/calendar/meta.yml b/apps/calendar/meta.yml index b4233d6..379f8a7 100644 --- a/apps/calendar/meta.yml +++ b/apps/calendar/meta.yml @@ -13,6 +13,8 @@ namespaces: event-location: description: + type: integer event-location: description: + type: integer diff --git a/src/db.rs b/src/db.rs index 44a6175..2519e19 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,20 +1,92 @@ -use std::path::Path; +use std::{ + collections::HashSet, + fs, + path::{Path, PathBuf}, +}; use anyhow::Result; use redb::ReadableTable; use uuid::Uuid; -use crate::db_keys::{metadata_by_id, nodes_by_id}; +use crate::{ + apps::meta::{self, AppMetadata}, + db_keys::{installed_apps, metadata_by_id, nodes_by_id}, +}; pub struct PanoramaDb { redb_db: redb::Database, + app_path: PathBuf, } impl PanoramaDb { - pub fn open(path: impl AsRef) -> Result { - let redb_db = redb::Database::create(path)?; + pub fn open( + db_path: impl AsRef, + app_path: impl AsRef, + ) -> Result { + let redb_db = redb::Database::create(db_path)?; + let app_path = app_path.as_ref().to_path_buf(); + let mut panorama = Self { redb_db, app_path }; - Ok(Self { redb_db }) + panorama.app_init()?; + + Ok(panorama) + } + + pub fn app_init(&mut self) -> Result<()> { + // Get list of on-disk apps + let on_disk_apps = { + let mut set = HashSet::new(); + for entry in fs::read_dir(&self.app_path)? { + let entry = entry?; + let path = entry.path(); + if !path.is_dir() { + continue; + } + + let meta_file = path.join("meta.yml"); + if !path.exists() { + continue; + } + + let meta_content = fs::read_to_string(&meta_file)?; + let parsed_toml: AppMetadata = serde_yaml::from_str(&meta_content)?; + + set.insert(parsed_toml.name); + } + set + }; + println!("On-disk apps: {on_disk_apps:?}"); + + // Get list of installed apps (in the database) + let installed_apps = { + let mut set = HashSet::new(); + let tx = self.redb_db.begin_read()?; + let table = tx.open_table(*installed_apps()); + if let Ok(installed_apps_table) = table { + for entry in installed_apps_table.iter()? { + let (key, value) = entry?; + let key = key.value(); + let value = value.value(); + set.insert(key.to_string()); + } + } + set + }; + println!("Installed apps: {installed_apps:?}"); + + // Add new apps to database + let new_apps = on_disk_apps + .difference(&installed_apps) + .collect::>(); + println!("New apps: {new_apps:?}"); + + // Remove existing apps + let deleted_apps = installed_apps + .difference(&on_disk_apps) + .collect::>(); + println!("Deleted apps: {deleted_apps:?}"); + + Ok(()) } pub fn check(&mut self) -> Result<()> { diff --git a/src/db_keys.rs b/src/db_keys.rs index 80e9d1e..508ac53 100644 --- a/src/db_keys.rs +++ b/src/db_keys.rs @@ -2,7 +2,8 @@ use once_cell::sync::OnceCell; use redb::TableDefinition; macro_rules! table_def { - ($str:ident, $key:ty, $val:ty) => { + ($(#[$meta:meta])* $str:ident, $key:ty, $val:ty) => { + $(#[$meta])* pub fn $str() -> &'static TableDefinition<'static, $key, $val> { static INSTANCE: OnceCell> = OnceCell::new(); @@ -19,3 +20,12 @@ table_def!( (&'static str, &'static str, &'static str), () ); + +table_def!( + /// Table of installed apps + /// + /// Maps "name" to (path installed from, json config) + installed_apps, + &'static str, + (&'static str, &'static str) +); diff --git a/src/main.rs b/src/main.rs index bed3929..d748758 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ fn main() -> Result<()> { fs::create_dir_all(&panorama_data_dir)?; let database_file_path = panorama_data_dir.join("data.redb"); - let database = PanoramaDb::open(database_file_path)?; + let database = PanoramaDb::open(database_file_path, "./apps")?; { let write = database.begin_write()?;