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_contains_key;
|
||||||
pub mod catalog_delete;
|
pub mod catalog_delete;
|
||||||
pub mod catalog_get;
|
pub mod catalog_get;
|
||||||
|
pub mod catalog_get_by;
|
||||||
pub mod catalog_init;
|
pub mod catalog_init;
|
||||||
pub mod catalog_insert_many;
|
pub mod catalog_insert_many;
|
||||||
pub mod catalog_is_empty;
|
pub mod catalog_is_empty;
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ impl From<Item> for Entity {
|
|||||||
let mut entity = Entity::new::<Item>()
|
let mut entity = Entity::new::<Item>()
|
||||||
.with_id(&value.id)
|
.with_id(&value.id)
|
||||||
.with_ref_date(value.first_seen)
|
.with_ref_date(value.first_seen)
|
||||||
.with_subclass("subitem")
|
|
||||||
.with_attribute("name", value.name)
|
.with_attribute("name", value.name)
|
||||||
.with_attribute("price", value.price)
|
.with_attribute("price", value.price)
|
||||||
.with_attribute("discount", value.discount)
|
.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 {
|
let item4 = Item {
|
||||||
id: "item-4".to_string(),
|
id: "item-4".to_string(),
|
||||||
subclass: None, // -> "subitem"
|
subclass: Some("subitem".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let another_item = AnotherItem {
|
let another_item = AnotherItem {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod catalog_delete;
|
pub mod catalog_delete;
|
||||||
pub mod catalog_get;
|
pub mod catalog_get;
|
||||||
|
pub mod catalog_get_by;
|
||||||
pub mod catalog_init;
|
pub mod catalog_init;
|
||||||
pub mod catalog_insert_many;
|
pub mod catalog_insert_many;
|
||||||
pub mod catalog_integration;
|
pub mod catalog_integration;
|
||||||
|
|||||||
Reference in New Issue
Block a user