Show HN: Heave: an EAV data model rust implementation

1 month ago 7

A Rust EAV data model implementation.

Heave is a Rust library that provides a flexible and extensible implementation of the Entity-Attribute-Value (EAV) data model. It allows you to manage data with a dynamic schema, making it suitable for scenarios where attributes of entities are not known at compile time. The library includes support for persisting the EAV data to a SQLite database.

Typed entities may be used leveraging the EAV trait to map to and from schemaless entities.

Learn more on EAV data model here.

  • 01.workspace: this folder contains all workspace crates
  • src/fun: contains all functions
  • src/imp: contains all implementation (impl blocks)
  • src/mcr: contains all macros
  • src/str: contains all structures
  • src/trt: contains all traits
  • src/tst: contains all tests
  • lib.rs: renames and re-exports code compnents

Here is an example of how to define a typed entity, persist it to a SQLite database, and load it back.

use heave::{Catalog, EAV, Entity}; use std::fs; // Define a typed entity. #[derive(Clone, Debug, PartialEq)] struct Product { id: String, name: String, model: Option<String>, price: u64, in_stock: bool, } // Implement the EAV trait to define the class of the entity. impl EAV for Product { fn class() -> &'static str { "product" } } // Implement From<Entity> to map a schemaless entity to a typed one. impl From<Entity> for Product { fn from(entity: Entity) -> Self { Product { id: entity.id.clone(), name: entity.unwrap("name"), model: entity.unwrap_opt("model"), price: entity.unwrap_or("price", 0), in_stock: entity.unwrap("in_stock"), } } } // Implement From<Product> to map a typed entity to a schemaless one. impl From<Product> for Entity { fn from(value: Product) -> Self { let mut entity = Entity::new::<Product>() .with_id(&value.id) .with_attribute("name", value.name) .with_attribute("price", value.price) .with_attribute("in_stock", value.in_stock); if let Some(model) = value.model { entity.set("model", model); } entity } } fn main() { let db_path = "example.db"; // Initialize a new catalog and database. let mut catalog = Catalog::new(db_path); catalog.init(); // Create a new typed object. let product = Product { id: "prod-123".to_string(), name: String::from("PenguinX Laptop"), model: None, price: 135000, in_stock: true, }; let product_id = product.id.clone(); // Insert the object and persist it to the database. catalog.insert(product); catalog.persist(); // Create a new catalog to simulate a different application instance. let mut new_catalog = Catalog::new(db_path); // Load the entity from the database. new_catalog.load_by_id(&product_id); // Retrieve the typed object from the catalog. let read_product = new_catalog.get::<Product>(&product_id).unwrap(); println!("Successfully persisted and loaded: {:?}", read_product); // Clean up the database file. fs::remove_file(db_path).unwrap(); }

Contributions are welcome! If you'd like to contribute to heave, please follow these steps:

  1. Fork the repository.
  2. Create a new branch for your feature or bug fix.
  3. Make your changes and commit them with a clear message.
  4. Push your branch to your fork.
  5. Open a pull request to the main repository.

Please ensure that your code adheres to the existing style and that all tests pass before submitting a pull request.

Read Entire Article