From 90fa4119ddf2d144585f752b2fb74be550a20bae Mon Sep 17 00:00:00 2001 From: davidemazzocchi Date: Mon, 29 Sep 2025 16:41:44 +0200 Subject: [PATCH] feat: add catalog persistance --- .gitignore | 1 + 01.workspace/heave/src/fun/mod.rs | 1 + 01.workspace/heave/src/fun/sqlite_init_db.rs | 5 +- .../heave/src/fun/sqlite_persist_catalog.rs | 57 +++++++++++++++++++ 01.workspace/heave/src/lib.rs | 3 + 01.workspace/heave/src/str/catalog.rs | 7 ++- 01.workspace/heave/src/str/entity.rs | 13 +++++ 01.workspace/heave/src/str/value.rs | 13 +++++ .../heave/src/tst/rusty_budger_use.rs | 8 ++- 9 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 01.workspace/heave/src/fun/sqlite_persist_catalog.rs diff --git a/.gitignore b/.gitignore index b34314e..6351568 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ 01.workspace/target +01.workspace/heave/rustybudger.sqlite3 diff --git a/01.workspace/heave/src/fun/mod.rs b/01.workspace/heave/src/fun/mod.rs index ca2006a..5f41027 100644 --- a/01.workspace/heave/src/fun/mod.rs +++ b/01.workspace/heave/src/fun/mod.rs @@ -1 +1,2 @@ pub mod sqlite_init_db; +pub mod sqlite_persist_catalog; diff --git a/01.workspace/heave/src/fun/sqlite_init_db.rs b/01.workspace/heave/src/fun/sqlite_init_db.rs index c6831ef..f6a8e99 100644 --- a/01.workspace/heave/src/fun/sqlite_init_db.rs +++ b/01.workspace/heave/src/fun/sqlite_init_db.rs @@ -8,13 +8,14 @@ pub fn run(path: &path::Path) { class TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS attribute ( - id TEXT PRIMARY KEY, + id TEXT, + entity_id TEXT, value_int INTEGER, value_uint INTEGER, value_real REAL, value_text TEXT, value_bool BOOL, - entity_id TEXT, + CONSTRAINT pk_id PRIMARY KEY (id, entity_id), CONSTRAINT fk_entity_id FOREIGN KEY (entity_id) REFERENCES entity (id) ON DELETE CASCADE ON UPDATE CASCADE ); "#; diff --git a/01.workspace/heave/src/fun/sqlite_persist_catalog.rs b/01.workspace/heave/src/fun/sqlite_persist_catalog.rs new file mode 100644 index 0000000..d6fad6e --- /dev/null +++ b/01.workspace/heave/src/fun/sqlite_persist_catalog.rs @@ -0,0 +1,57 @@ +use crate::*; +use rusqlite::*; + +fn column(value: &Value) -> &'static str { + match value { + Value::SignedInt(_) => "value_int", + Value::UnsignedInt(_) => "value_uint", + Value::Real(_) => "value_real", + Value::Text(_) => "value_text", + Value::Bool(_) => "value_bool", + } +} + +pub fn run(path: &path::Path, catalog: &Catalog) { + let mut connection = Connection::open(path).unwrap(); + let delete_entity_statement = r#" + DELETE FROM entity + WHERE entity.id = ?1; + "#; + let insert_entity_statement = r#" + INSERT INTO entity (id, class) + VALUES (?1, ?2); + "#; + let insert_attribute_statement_template = r#" + INSERT INTO attribute (id, entity_id, {column}) + VALUES (?1, ?2, ?3); + "#; + let transaction = connection.transaction().unwrap(); + for (_key, entity) in catalog.items.iter() { + let _ = transaction.execute(delete_entity_statement, [&entity.id]); + let _ = transaction.execute(insert_entity_statement, (&entity.id, &entity.class)); + for (_key, attribute) in entity.attributes.iter() { + let insert_attribute_statement = + insert_attribute_statement_template.replace("{column}", column(&attribute.value)); + let _ = transaction.execute( + &insert_attribute_statement, + (&attribute.id, &entity.id, &attribute.value.to_string()), + ); + } + } + let _ = transaction.commit(); +} + +#[cfg(test)] +mod unit_tests { + use super::*; + #[test] + fn test_call() { + let tempfile = tempfile::NamedTempFile::new().unwrap(); + let path = tempfile.path(); + let entity = Entity::new("test"); + let mut catalog = Catalog::new(""); + catalog.insert(entity); + sqlite::init::db(path); + run(path, &catalog); + } +} diff --git a/01.workspace/heave/src/lib.rs b/01.workspace/heave/src/lib.rs index 6552273..644e03f 100644 --- a/01.workspace/heave/src/lib.rs +++ b/01.workspace/heave/src/lib.rs @@ -20,4 +20,7 @@ mod sqlite { pub mod init { pub use crate::fun::sqlite_init_db::run as db; } + pub mod persist { + pub use crate::fun::sqlite_persist_catalog::run as catalog; + } } diff --git a/01.workspace/heave/src/str/catalog.rs b/01.workspace/heave/src/str/catalog.rs index ded8b09..654883e 100644 --- a/01.workspace/heave/src/str/catalog.rs +++ b/01.workspace/heave/src/str/catalog.rs @@ -3,11 +3,12 @@ use crate::*; #[derive(Debug, Default, PartialEq, Clone)] pub struct O { path: String, - items: std::collections::HashMap, + pub(crate) items: std::collections::HashMap, } impl O { pub fn new(path: &str) -> Self { + sqlite::init::db(path::Path::new(path)); Self { path: String::from(path), ..O::default() @@ -34,6 +35,10 @@ impl O { let entity = self.items.get(id); entity.map(|e| T::from_eav(e.clone())) } + pub fn persist(&self) { + let path = path::Path::new(&self.path); + sqlite::persist::catalog(path, self); + } } // impl std::fmt::Display for O { diff --git a/01.workspace/heave/src/str/entity.rs b/01.workspace/heave/src/str/entity.rs index e1a57f0..df48298 100644 --- a/01.workspace/heave/src/str/entity.rs +++ b/01.workspace/heave/src/str/entity.rs @@ -7,6 +7,19 @@ pub struct O { pub attributes: std::collections::HashMap, } +impl EAV for Entity {} + +impl ToEAV for Entity { + fn to_eav(self) -> Entity { + self + } +} +impl FromEAV for Entity { + fn from_eav(entity: Entity) -> Self { + entity + } +} + impl O { pub fn new(class: &str) -> Self { Self { diff --git a/01.workspace/heave/src/str/value.rs b/01.workspace/heave/src/str/value.rs index 484bea7..4d8861d 100644 --- a/01.workspace/heave/src/str/value.rs +++ b/01.workspace/heave/src/str/value.rs @@ -8,3 +8,16 @@ pub enum E { Text(String), UnsignedInt(u64), } + +impl std::fmt::Display for E { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let string_value = match self { + Value::Bool(value) => value.to_string(), + Value::Real(value) => value.to_string(), + Value::SignedInt(value) => value.to_string(), + Value::UnsignedInt(value) => value.to_string(), + Value::Text(value) => value.clone(), + }; + write!(f, "{}", string_value) + } +} diff --git a/01.workspace/heave/src/tst/rusty_budger_use.rs b/01.workspace/heave/src/tst/rusty_budger_use.rs index bce6582..680b437 100644 --- a/01.workspace/heave/src/tst/rusty_budger_use.rs +++ b/01.workspace/heave/src/tst/rusty_budger_use.rs @@ -87,7 +87,12 @@ mod tests { } #[test] pub fn check_001() { - let mut catalog = Catalog::new(""); + let file = "./rustybudger.sqlite3"; + // clean filesystem + if let Ok(true) = std::fs::exists(file) { + let _ = std::fs::remove_file(file); + } + let mut catalog = Catalog::new(file); let operation_01 = Operation { id: short_uuid::short!().to_string(), date: 20250929, @@ -115,5 +120,6 @@ mod tests { catalog.insert_many(operations); catalog.insert_many(categories); catalog.insert_many(relations); + catalog.persist(); } }