feat: add catalog persistance
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
01.workspace/target
|
||||
01.workspace/heave/rustybudger.sqlite3
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod sqlite_init_db;
|
||||
pub mod sqlite_persist_catalog;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
"#;
|
||||
|
||||
57
01.workspace/heave/src/fun/sqlite_persist_catalog.rs
Normal file
57
01.workspace/heave/src/fun/sqlite_persist_catalog.rs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ use crate::*;
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub struct O {
|
||||
path: String,
|
||||
items: std::collections::HashMap<String, Entity>,
|
||||
pub(crate) items: std::collections::HashMap<String, Entity>,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -7,6 +7,19 @@ pub struct O {
|
||||
pub attributes: std::collections::HashMap<String, Attribute>,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user