davidemazzocchi bc4ac445c9 feat: set catalog as thread safe
wip: add a thread safe way to access items; change upsert

wip: change catalog.delete to be thread safe

wip: change catalog.load_by_* to be thread safe

wip: add a thread safe way to access items in readonly mode; change
catalog.get

wip: change catalog.persist to be thread safe

wip: disable examples temprarily

wip: remove catalog_new.rs file

wip: ignore result of iteration

wip: disable lib example

wip: enable catalog.delete tests

wip: enable catalog.get tests

wip enalbe catalog.init tests

wip: enable catalog.insert_many tests

wip: enable catalog.load_by_class tests

wip: enable catalog.load_by_id tests

wip: add len() and is_empty() convenience thread safe method to catalog

wip: use convenience methods for len and is_empty checks

wip: enable catalog.load_by_id and catalog.new tests

wip: enable catalog.upsert tests

wip: enable catalog.persist test; add catalog.contains_key

wip: enable catalog.load_by_filter tests

wip: enable sqlite_* tests

wip: enable list_by_class with tests

wip: enable list_by_class_and_subclass with tests

wip: enable integration tests

wip: switch iter to into_iter for tests
2026-03-09 08:39:13 +01:00
2026-03-09 08:39:08 +01:00
2026-03-09 08:39:07 +01:00

heave 󱅝

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.

🚧 A Note From the Developer 🚧

Thank you for checking out Heave! I'm so excited you're here and interested in my project at this early stage.

I want to be upfront: this is my work-in-progress, and I'm still building and experimenting a lot. This means things are changing constantly. As you dive in, please keep in mind:

  • I'll be making frequent breaking changes. I'm still shaping the core API, so I'll be refactoring things often. Your code will likely break between updates.
  • You'll find bugs. I'm working hard, but things might not always work as expected. I'd be incredibly grateful if you reported any issues you find!
  • Features are incomplete. Many parts of the project are still on my drawing board or only partially implemented.

Because of all this, I'm asking you to use Heave for evaluation and feedback only. I strongly recommend not using it in a production environment just yet.

Your feedback is invaluable in helping me shape the future of Heave. I'd love to hear from you, so please feel free to open an issue!

Typed Entities

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

EAV

Learn more on EAV data model here.

Workspace Structure

  • 01.workspace: this folder contains all workspace crates

heave crate structure

  • 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

Example

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

use heave::*;

// Define a struct named `Product` to represent a product.
struct Product {
    // `id` is a public field of type `String` to uniquely identify the product.
    pub id: String,
    // `name` is a public field of type `String` for the product's name.
    pub name: String,
    // `model` is a public optional field of type `String` for the product's model.
    pub model: Option<String>,
    // `price` is a public field of type `i64` for the product's price.
    pub price: i64,
}

// Implement the `EAV` trait for the `Product` struct.
impl EAV for Product {
    // `class` is a function that returns the class name of the entity.
    fn class() -> &'static str {
        "product"
    }
}

// Implement the `From<Product>` trait for the `Entity` struct.
impl From<Product> for Entity {
    // `from` is a function that converts a `Product` into an `Entity`.
    fn from(value: Product) -> Entity {
        // Create a new `Entity` for the `Product` class.
        Entity::new::<Product>()
            // Set the entity's ID from the product's ID.
            .with_id(&value.id)
            // Add the "name" attribute with the product's name.
            .with_attribute("name", value.name)
            // Add the optional "model" attribute with the product's model.
            .with_opt_attribute("model", value.model)
            // Add the "price" attribute with the product's price.
            .with_attribute("price", value.price)
    }
}

// Implement the `From<Entity>` trait for the `Product` struct.
impl From<Entity> for Product {
    // `from` is a function that converts an `Entity` into a `Product`.
    fn from(value: Entity) -> Self {
        // Create a new `Product` from the entity's attributes.
        Self {
            // Set the product's ID from the entity's ID.
            id: value.id.clone(),
            // Unwrap the "name" attribute to get the product's name.
            name: value.unwrap("name"),
            // Unwrap the optional "model" attribute to get the product's model.
            model: value.unwrap_opt("model"),
            // Unwrap the "price" attribute to get the product's price.
            price: value.unwrap("price"),
        }
    }
}

fn main() {
    // Define the path for the SQLite database file.
    let db_path = "./simple_product.sqlite3";
    // Create a new `Catalog` instance with the specified database path.
    let mut catalog = Catalog::new(db_path);
    // Initialize the catalog, which sets up the database.
    catalog.init().unwrap();
    // Create a new `Product` instance representing a laptop.
    let new_laptop = Product {
        id: "LT001".to_string(),
        name: "SuperPenguin".to_string(),
        model: Some("Mark III.2".to_string()),
        price: 125000,
    };
    // Insert the new laptop into the catalog. Note that at this time the product is in memory.
    catalog.upsert(new_laptop);
    // Persist the changes in the catalog to the database.
    catalog.persist().unwrap();
    // Remove the SQLite database file.
    std::fs::remove_file(db_path).unwrap();
}

Contributing

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 (feature/my_awesome_feature) or bug fix (fix/my_awesome_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.

Credits

Description
A Rust EAV data model implementation for SQLite
Readme MIT 372 KiB
Languages
Rust 100%