feat: add subclass to entity struct
This commit is contained in:
@@ -6,6 +6,7 @@ pub fn run(path: &path::Path) -> result::Result<(), FailedTo> {
|
|||||||
CREATE TABLE IF NOT EXISTS entity (
|
CREATE TABLE IF NOT EXISTS entity (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
class TEXT NOT NULL,
|
class TEXT NOT NULL,
|
||||||
|
subclass TEXT,
|
||||||
ref_date INTEGER
|
ref_date INTEGER
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS attribute (
|
CREATE TABLE IF NOT EXISTS attribute (
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ mod tests {
|
|||||||
let mut entity = Entity {
|
let mut entity = Entity {
|
||||||
id: "entity1".to_string(),
|
id: "entity1".to_string(),
|
||||||
class: "class1".to_string(),
|
class: "class1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: Default::default(),
|
state: Default::default(),
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
@@ -77,6 +78,7 @@ mod tests {
|
|||||||
let mut entity = Entity {
|
let mut entity = Entity {
|
||||||
id: "entity2".to_string(),
|
id: "entity2".to_string(),
|
||||||
class: "class1".to_string(),
|
class: "class1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: Default::default(),
|
state: Default::default(),
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
@@ -101,6 +103,7 @@ mod tests {
|
|||||||
let mut entity = Entity {
|
let mut entity = Entity {
|
||||||
id: "entity3".to_string(),
|
id: "entity3".to_string(),
|
||||||
class: "class1".to_string(),
|
class: "class1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: Default::default(),
|
state: Default::default(),
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ use crate::*;
|
|||||||
pub fn run(row: &rusqlite::Row) -> rusqlite::Result<Entity> {
|
pub fn run(row: &rusqlite::Row) -> rusqlite::Result<Entity> {
|
||||||
let id: String = row.get(0)?;
|
let id: String = row.get(0)?;
|
||||||
let class: String = row.get(1)?;
|
let class: String = row.get(1)?;
|
||||||
let ref_date: Option<u64> = row.get(2)?;
|
let subclass: Option<String> = row.get(2)?;
|
||||||
|
let ref_date: Option<u64> = row.get(3)?;
|
||||||
let entity = Entity {
|
let entity = Entity {
|
||||||
id,
|
id,
|
||||||
state: EntityState::Loaded,
|
state: EntityState::Loaded,
|
||||||
class,
|
class,
|
||||||
|
subclass,
|
||||||
ref_date,
|
ref_date,
|
||||||
attributes: std::collections::HashMap::new(),
|
attributes: std::collections::HashMap::new(),
|
||||||
};
|
};
|
||||||
@@ -26,9 +28,10 @@ mod tests {
|
|||||||
fn map_row_to_entity_should_correctly_map_valid_row() {
|
fn map_row_to_entity_should_correctly_map_valid_row() {
|
||||||
// Verifies that a valid database row is correctly mapped to an Entity struct with all fields populated.
|
// Verifies that a valid database row is correctly mapped to an Entity struct with all fields populated.
|
||||||
let conn = Connection::open_in_memory().unwrap();
|
let conn = Connection::open_in_memory().unwrap();
|
||||||
let entity = get_entity_from_query(&conn, "SELECT 'e1', 'c1', 12345").unwrap();
|
let entity = get_entity_from_query(&conn, "SELECT 'e1', 'c1', 's1', 12345").unwrap();
|
||||||
assert_eq!(entity.id, "e1");
|
assert_eq!(entity.id, "e1");
|
||||||
assert_eq!(entity.class, "c1");
|
assert_eq!(entity.class, "c1");
|
||||||
|
assert_eq!(entity.subclass, Some("s1".to_string()));
|
||||||
assert_eq!(entity.ref_date, Some(12345));
|
assert_eq!(entity.ref_date, Some(12345));
|
||||||
assert_eq!(entity.state, EntityState::Loaded);
|
assert_eq!(entity.state, EntityState::Loaded);
|
||||||
assert!(entity.attributes.is_empty());
|
assert!(entity.attributes.is_empty());
|
||||||
@@ -37,9 +40,20 @@ mod tests {
|
|||||||
fn map_row_to_entity_should_handle_null_ref_date() {
|
fn map_row_to_entity_should_handle_null_ref_date() {
|
||||||
// Ensures that a row with a NULL 'ref_date' is successfully mapped to an Entity with 'ref_date' as None.
|
// Ensures that a row with a NULL 'ref_date' is successfully mapped to an Entity with 'ref_date' as None.
|
||||||
let conn = Connection::open_in_memory().unwrap();
|
let conn = Connection::open_in_memory().unwrap();
|
||||||
let entity = get_entity_from_query(&conn, "SELECT 'e2', 'c2', NULL").unwrap();
|
let entity = get_entity_from_query(&conn, "SELECT 'e2', 'c2', 's2', NULL").unwrap();
|
||||||
assert_eq!(entity.id, "e2");
|
assert_eq!(entity.id, "e2");
|
||||||
assert_eq!(entity.class, "c2");
|
assert_eq!(entity.class, "c2");
|
||||||
|
assert_eq!(entity.subclass, Some("s2".to_string()));
|
||||||
|
assert_eq!(entity.ref_date, None);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn map_row_to_entity_should_handle_null_subclass() {
|
||||||
|
// Ensures that a row with a NULL 'subclass' is successfully mapped to an Entity with 'subclass' as None.
|
||||||
|
let conn = Connection::open_in_memory().unwrap();
|
||||||
|
let entity = get_entity_from_query(&conn, "SELECT 'e2', 'c2', NULL, NULL").unwrap();
|
||||||
|
assert_eq!(entity.id, "e2");
|
||||||
|
assert_eq!(entity.class, "c2");
|
||||||
|
assert_eq!(entity.subclass, None);
|
||||||
assert_eq!(entity.ref_date, None);
|
assert_eq!(entity.ref_date, None);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ mod tests {
|
|||||||
let mut entity = Entity {
|
let mut entity = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: EntityState::New,
|
state: EntityState::New,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
@@ -141,6 +142,7 @@ mod tests {
|
|||||||
let entity = Entity {
|
let entity = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: EntityState::ToDelete,
|
state: EntityState::ToDelete,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
@@ -161,6 +163,7 @@ mod tests {
|
|||||||
let to_delete = Entity {
|
let to_delete = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: EntityState::ToDelete,
|
state: EntityState::ToDelete,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
@@ -168,6 +171,7 @@ mod tests {
|
|||||||
let to_add = Entity {
|
let to_add = Entity {
|
||||||
id: "e2".to_string(),
|
id: "e2".to_string(),
|
||||||
class: "c2".to_string(),
|
class: "c2".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: EntityState::New,
|
state: EntityState::New,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
@@ -190,6 +194,7 @@ mod tests {
|
|||||||
let unmodified = Entity {
|
let unmodified = Entity {
|
||||||
id: "e1".to_string(),
|
id: "e1".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: EntityState::Loaded,
|
state: EntityState::Loaded,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
@@ -213,6 +218,7 @@ mod tests {
|
|||||||
let mut new_entity = Entity {
|
let mut new_entity = Entity {
|
||||||
id: "e_new".to_string(),
|
id: "e_new".to_string(),
|
||||||
class: "c1".to_string(),
|
class: "c1".to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: HashMap::new(),
|
attributes: HashMap::new(),
|
||||||
state: EntityState::New,
|
state: EntityState::New,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
|
|||||||
@@ -32,8 +32,9 @@
|
|||||||
//! and the necessary `From` and `TryFrom` conversions.
|
//! and the necessary `From` and `TryFrom` conversions.
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use heave::{EAV, Catalog, Entity, Value, FailedTo, Comparison, Filter};
|
//! use heave::*;
|
||||||
//! use std::convert::{From, TryFrom};
|
//! use std::convert::{From, TryFrom};
|
||||||
|
//! use std::result::Result;
|
||||||
//!
|
//!
|
||||||
//! // Define a simple struct representing a product.
|
//! // Define a simple struct representing a product.
|
||||||
//! #[derive(Debug, Default, PartialEq, Clone)]
|
//! #[derive(Debug, Default, PartialEq, Clone)]
|
||||||
@@ -68,10 +69,10 @@
|
|||||||
//!
|
//!
|
||||||
//! fn try_from(entity: Entity) -> Result<Self, Self::Error> {
|
//! fn try_from(entity: Entity) -> Result<Self, Self::Error> {
|
||||||
//! Ok(Self {
|
//! Ok(Self {
|
||||||
//! id: entity.id,
|
//! id: entity.id.clone(),
|
||||||
//! name: entity.unwrap("name").ok_or(FailedTo::ConvertEntity)?,
|
//! name: entity.unwrap("name").map_err(|_| FailedTo::ConvertEntity)?,
|
||||||
//! price: entity.unwrap("price").ok_or(FailedTo::ConvertEntity)?,
|
//! price: entity.unwrap("price").map_err(|_| FailedTo::ConvertEntity)?,
|
||||||
//! in_stock: entity.unwrap("in_stock").ok_or(FailedTo::ConvertEntity)?,
|
//! in_stock: entity.unwrap("in_stock").map_err(|_| FailedTo::ConvertEntity)?,
|
||||||
//! })
|
//! })
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
|||||||
@@ -161,6 +161,24 @@ impl Catalog {
|
|||||||
.filter(move |item| item.class == T::class())
|
.filter(move |item| item.class == T::class())
|
||||||
.map(|item| T::try_from(item.clone()).map_err(|_| FailedTo::ConvertEntity))
|
.map(|item| T::try_from(item.clone()).map_err(|_| FailedTo::ConvertEntity))
|
||||||
}
|
}
|
||||||
|
/// Returns an iterator over all entities of a specific class and subclass
|
||||||
|
/// in the in-memory catalog.
|
||||||
|
///
|
||||||
|
/// This is a purely in-memory operation and does not interact with the database.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An iterator that yields items of type `T` from the in-memory cache.
|
||||||
|
pub fn list_by_class_and_subclass<T>(&self, subclass: &str) -> impl Iterator<Item = Result<T, FailedTo>>
|
||||||
|
where
|
||||||
|
T: EAV,
|
||||||
|
{
|
||||||
|
self.items
|
||||||
|
.values()
|
||||||
|
.filter(move |item| item.class == T::class())
|
||||||
|
.filter(move |item| item.subclass == Some(subclass.to_string()))
|
||||||
|
.map(|item| T::try_from(item.clone()).map_err(|_| FailedTo::ConvertEntity))
|
||||||
|
}
|
||||||
/// Returns an iterator over entities in the in-memory catalog that match a given
|
/// Returns an iterator over entities in the in-memory catalog that match a given
|
||||||
/// class, attribute, and value.
|
/// class, attribute, and value.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ pub struct O {
|
|||||||
/// A string identifying the "type" or "class" of the entity (e.g., "product", "user").
|
/// A string identifying the "type" or "class" of the entity (e.g., "product", "user").
|
||||||
/// This is used to group and query entities of the same kind.
|
/// This is used to group and query entities of the same kind.
|
||||||
pub class: String,
|
pub class: String,
|
||||||
|
/// A string identifying the "subtype" or "subclass" of the entity (e.g.,
|
||||||
|
/// "computer", "phone", "customer"). This is used to group and query entities
|
||||||
|
/// of different subtype but of the same kind or inside the same domain (class)
|
||||||
|
pub subclass: Option<String>,
|
||||||
/// A map of attribute names to `Attribute` values, holding the actual data
|
/// A map of attribute names to `Attribute` values, holding the actual data
|
||||||
/// of the entity.
|
/// of the entity.
|
||||||
pub attributes: std::collections::HashMap<String, Attribute>,
|
pub attributes: std::collections::HashMap<String, Attribute>,
|
||||||
@@ -34,6 +38,7 @@ impl Entity {
|
|||||||
state: EntityState::New,
|
state: EntityState::New,
|
||||||
ref_date: None,
|
ref_date: None,
|
||||||
class: T::class().to_string(),
|
class: T::class().to_string(),
|
||||||
|
subclass: None,
|
||||||
attributes: std::collections::HashMap::<String, Attribute>::new(),
|
attributes: std::collections::HashMap::<String, Attribute>::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,6 +57,11 @@ impl Entity {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_subclass(mut self, subclass: &str) -> Self {
|
||||||
|
self.subclass = Some(subclass.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the reference date of the entity.
|
/// Sets the reference date of the entity.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|||||||
Reference in New Issue
Block a user