ferroui_base/collections/
ferro_dictionary_extensions.rs1use super::ferro_list_extensions::weak_subscribe;
2use super::{CollectionChangedHandler, IFerroReadOnlyDictionary, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs};
3use crate::reactive::{Disposable, IDisposable};
4use crate::utilities::WeakEventSender;
5use std::rc::Rc;
6
7pub struct FerroDictionaryExtensions;
9
10impl FerroDictionaryExtensions {
11 pub fn for_each_item<K, V, D>(
24 collection: &D,
25 added: impl Fn(&K, &V) + 'static,
26 removed: impl Fn(&K, &V) + 'static,
27 reset: impl Fn() + 'static,
28 weak_subscription: bool,
29 ) -> Rc<dyn IDisposable>
30 where
31 K: Clone + 'static,
32 V: Clone + 'static,
33 D: IFerroReadOnlyDictionary<K, V> + WeakEventSender,
34 {
35 let add = Rc::new(move |items: &[(K, V)]| {
36 for (key, value) in items {
37 added(key, value);
38 }
39 });
40
41 let remove = move |items: &[(K, V)]| {
42 for (key, value) in items {
43 removed(key, value);
44 }
45 };
46
47 let source = collection.downgrade_sender();
50 let add_items = add.clone();
51 let handler: Rc<CollectionChangedHandler<(K, V)>> =
52 Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, (K, V)>| match e.action {
53 NotifyCollectionChangedAction::Add => add_items(e.new_items),
54
55 NotifyCollectionChangedAction::Move | NotifyCollectionChangedAction::Replace => {
56 remove(e.old_items);
57 add_items(e.new_items);
58 }
59
60 NotifyCollectionChangedAction::Remove => remove(e.old_items),
61
62 NotifyCollectionChangedAction::Reset => {
63 reset();
64 if let Some(collection) = D::upgrade_sender(&source) {
65 add_items(&collection.to_vec());
66 }
67 }
68 });
69
70 add(&collection.to_vec());
71
72 if weak_subscription {
73 weak_subscribe(collection, handler, D::add_collection_changed, D::remove_collection_changed)
74 } else {
75 let token = collection.add_collection_changed(handler);
76
77 let collection = collection.clone();
78 Disposable::create(move || {
79 collection.remove_collection_changed(token);
80 })
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
91 use crate::collections::FerroDictionary;
92 use std::cell::RefCell;
93
94 #[test]
95 fn for_each_item_reports_initial_added_replaced_and_removed_entries() {
96 let dictionary = FerroDictionary::from_dictionary([("a".to_string(), 1)]);
97 let log = Rc::new(RefCell::new(Vec::new()));
98 let (a, r, z) = (log.clone(), log.clone(), log.clone());
99
100 let subscription = FerroDictionaryExtensions::for_each_item(
101 &dictionary,
102 move |k: &String, v: &i32| a.borrow_mut().push(format!("+{k}={v}")),
103 move |k: &String, v: &i32| r.borrow_mut().push(format!("-{k}={v}")),
104 move || z.borrow_mut().push("reset".to_string()),
105 false,
106 );
107 dictionary.add("b".to_string(), 2);
108 dictionary.set("a".to_string(), 3);
109 dictionary.remove(&"b".to_string());
110 subscription.dispose();
111 dictionary.add("c".to_string(), 4);
112
113 assert_eq!(*log.borrow(), ["+a=1", "+b=2", "-a=1", "+a=3", "-b=2"].map(String::from).to_vec());
114 }
115}