feat: add catalog.get_by in order to get items given a predicate
This commit is contained in:
BIN
01.workspace/heave/get_by_finds_item.db
Normal file
BIN
01.workspace/heave/get_by_finds_item.db
Normal file
Binary file not shown.
BIN
01.workspace/heave/get_by_multiple_matches.db
Normal file
BIN
01.workspace/heave/get_by_multiple_matches.db
Normal file
Binary file not shown.
BIN
01.workspace/heave/get_by_on_empty_catalog_returns_none.db
Normal file
BIN
01.workspace/heave/get_by_on_empty_catalog_returns_none.db
Normal file
Binary file not shown.
BIN
01.workspace/heave/get_by_returns_none_when_no_match.db
Normal file
BIN
01.workspace/heave/get_by_returns_none_when_no_match.db
Normal file
Binary file not shown.
45
01.workspace/heave/src/imp/catalog_get_by.rs
Normal file
45
01.workspace/heave/src/imp/catalog_get_by.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::*;
|
||||
|
||||
impl Catalog {
|
||||
/// Retrieves the first entity from the in-memory catalog that satisfies a given predicate.
|
||||
///
|
||||
/// This is a purely in-memory operation and does not interact with the database.
|
||||
/// It iterates through all entities currently loaded in the catalog, attempts to
|
||||
/// convert each to the specified type `T`, and then applies the provided
|
||||
/// `predicate` function. The first entity that successfully converts and
|
||||
/// satisfies the predicate is returned.
|
||||
///
|
||||
/// Entities that fail to convert to type `T` are silently skipped and do not
|
||||
/// cause an error to be returned by this function.
|
||||
///
|
||||
/// # Type Parameters
|
||||
///
|
||||
/// * `T` - The target type to convert the entity into, which must implement the `EAV` trait.
|
||||
/// * `F` - The type of the predicate closure, which takes a reference to `T` and returns a `bool`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `predicate` - A closure that defines the condition an entity must meet to be returned.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An `Ok(Some(T))` containing the first converted entity that satisfies the predicate,
|
||||
/// or `Ok(None)` if no such entity is found or if all matching entities fail conversion.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err(FailedTo)` if an internal error occurs during the `with_items` operation
|
||||
/// (e.g., mutex poisoning).
|
||||
pub fn get_by<T, F>(&self, predicate: F) -> Result<Option<T>, FailedTo>
|
||||
where
|
||||
T: EAV,
|
||||
F: Fn(&T) -> bool,
|
||||
{
|
||||
self.with_items(|items| {
|
||||
Ok(items
|
||||
.values()
|
||||
.flat_map(|entity| T::try_from(entity.clone()))
|
||||
.find(|item| predicate(item)))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub mod bool_try_from_value;
|
||||
pub mod catalog_contains_key;
|
||||
pub mod catalog_delete;
|
||||
pub mod catalog_get;
|
||||
pub mod catalog_get_by;
|
||||
pub mod catalog_init;
|
||||
pub mod catalog_insert_many;
|
||||
pub mod catalog_is_empty;
|
||||
|
||||
@@ -29,7 +29,6 @@ impl From<Item> for Entity {
|
||||
let mut entity = Entity::new::<Item>()
|
||||
.with_id(&value.id)
|
||||
.with_ref_date(value.first_seen)
|
||||
.with_subclass("subitem")
|
||||
.with_attribute("name", value.name)
|
||||
.with_attribute("price", value.price)
|
||||
.with_attribute("discount", value.discount)
|
||||
|
||||
80
01.workspace/heave/src/tst/catalog_get_by.rs
Normal file
80
01.workspace/heave/src/tst/catalog_get_by.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
fn catalog(name: &str) -> Catalog {
|
||||
let path = format!("{}.db", name);
|
||||
let fs_path = path::Path::new(&path);
|
||||
if fs_path.exists() {
|
||||
std::fs::remove_file(fs_path).unwrap();
|
||||
}
|
||||
let catalog = Catalog::new(&path);
|
||||
catalog.init().unwrap();
|
||||
catalog
|
||||
}
|
||||
#[test]
|
||||
fn get_by_finds_item() {
|
||||
let mut catalog = catalog("get_by_finds_item");
|
||||
let item1 = Item {
|
||||
id: "1".to_string(),
|
||||
name: "one".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "2".to_string(),
|
||||
name: "two".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
catalog
|
||||
.insert_many(vec![item1.clone(), item2.clone()])
|
||||
.unwrap();
|
||||
let found = catalog
|
||||
.get_by(|item: &Item| item.name == "two")
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(found, item2);
|
||||
}
|
||||
#[test]
|
||||
fn get_by_returns_none_when_no_match() {
|
||||
let mut catalog = catalog("get_by_returns_none_when_no_match");
|
||||
let item1 = Item {
|
||||
id: "1".to_string(),
|
||||
name: "one".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
catalog.insert_many(vec![item1.clone()]).unwrap();
|
||||
let found = catalog.get_by(|item: &Item| item.name == "two").unwrap();
|
||||
assert!(found.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn get_by_on_empty_catalog_returns_none() {
|
||||
let catalog = catalog("get_by_on_empty_catalog_returns_none");
|
||||
let found = catalog.get_by(|item: &Item| item.name == "any").unwrap();
|
||||
assert!(found.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn get_by_multiple_matches() {
|
||||
let mut catalog = catalog("get_by_multiple_matches");
|
||||
let item1 = Item {
|
||||
id: "1".to_string(),
|
||||
name: "match".to_string(),
|
||||
price: 10,
|
||||
..Default::default()
|
||||
};
|
||||
let item2 = Item {
|
||||
id: "2".to_string(),
|
||||
name: "match".to_string(),
|
||||
price: 20,
|
||||
..Default::default()
|
||||
};
|
||||
catalog
|
||||
.insert_many(vec![item1.clone(), item2.clone()])
|
||||
.unwrap();
|
||||
// `get_by` should return the first match it finds. The order is not guaranteed
|
||||
// by the underlying HashMap, so we just check that it returns one of them.
|
||||
let found = catalog
|
||||
.get_by(|item: &Item| item.name == "match")
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(found == item1 || found == item2);
|
||||
}
|
||||
}
|
||||
@@ -1462,7 +1462,7 @@ mod tests {
|
||||
};
|
||||
let item4 = Item {
|
||||
id: "item-4".to_string(),
|
||||
subclass: None, // -> "subitem"
|
||||
subclass: Some("subitem".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
let another_item = AnotherItem {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod catalog_delete;
|
||||
pub mod catalog_get;
|
||||
pub mod catalog_get_by;
|
||||
pub mod catalog_init;
|
||||
pub mod catalog_insert_many;
|
||||
pub mod catalog_integration;
|
||||
|
||||
Reference in New Issue
Block a user