use std::hash::Hash;
use super::ProjectCellExt;
use crate::{
cell::CellImmutable,
cell_map::CellMap,
traits::{CellValue, Gettable, MapExt, Watchable},
};
pub trait SelectCellExt<K, V>
where
K: Hash + Eq + CellValue,
V: CellValue,
{
#[track_caller]
fn select_cell<W, F>(&self, predicate: F) -> CellMap<K, V, CellImmutable>
where
W: Watchable<bool> + Gettable<bool> + Clone + Send + Sync + 'static,
F: Fn(&K, &V) -> W + Send + Sync + 'static;
}
impl<K, V, M> SelectCellExt<K, V> for CellMap<K, V, M>
where
K: Hash + Eq + CellValue,
V: CellValue,
{
fn select_cell<W, F>(&self, predicate: F) -> CellMap<K, V, CellImmutable>
where
W: Watchable<bool> + Gettable<bool> + Clone + Send + Sync + 'static,
F: Fn(&K, &V) -> W + Send + Sync + 'static,
{
self.project_cell(move |k, v| {
let k = k.clone();
let v = v.clone();
predicate(&k, &v).map(move |include| {
if *include {
Some((k.clone(), v.clone()))
} else {
None
}
})
})
}
}
#[cfg(test)]
mod tests {
use std::sync::mpsc;
use super::*;
use crate::{Cell, MapExt, cell_map::MapDiff};
#[test]
fn select_cell_reacts_to_predicate_changes() {
let values = CellMap::<String, i32>::new();
let gates = CellMap::<String, bool>::new();
values.insert("a".to_string(), 10);
values.insert("b".to_string(), 20);
gates.insert("a".to_string(), false);
gates.insert("b".to_string(), true);
let filtered = values.select_cell({
let gates = gates.clone();
move |key, _value| gates.get(key).map(|v| v.unwrap_or(false))
});
assert_eq!(filtered.entries().get().len(), 1);
assert!(!filtered.contains_key(&"a".to_string()));
assert!(filtered.contains_key(&"b".to_string()));
gates.insert("a".to_string(), true);
assert_eq!(filtered.entries().get().len(), 2);
gates.insert("b".to_string(), false);
assert_eq!(filtered.entries().get().len(), 1);
}
#[test]
fn select_cell_preserves_upstream_batch_without_extra_emissions() {
let source = CellMap::<String, i32>::new();
let out = source.select_cell(|_, _| Cell::new(true).lock());
let (tx, rx) = mpsc::channel::<MapDiff<String, i32>>();
let _guard = out.subscribe_diffs(move |diff| {
let _ = tx.send(diff.clone());
});
source.insert_many(vec![("a".to_string(), 1), ("b".to_string(), 2)]);
let seen: Vec<_> = rx.try_iter().collect();
assert_eq!(seen.len(), 2);
match seen.last().unwrap() {
MapDiff::Batch { changes } => assert_eq!(changes.len(), 2),
_ => panic!("expected batch diff from select_cell"),
}
}
}