1use super::ferro_list::raise_collection_changed;
2use super::{CollectionChangedHandler, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs};
3use crate::data::core::INDEXER_NAME;
4use crate::data::model::{CollectionChange, Event, INotifyCollectionChanged, INotifyPropertyChanged};
5use crate::utilities::{HandlerList, WeakEventSender};
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::fmt::Display;
9use std::hash::Hash;
10use std::rc::{Rc, Weak};
11
12struct Inner<K, V> {
19 entries: Vec<Option<(K, V)>>,
20 free: Vec<usize>,
21 map: HashMap<K, usize>,
22}
23
24impl<K: Eq + Hash + Clone, V: Clone> Inner<K, V> {
25 fn with_capacity(capacity: usize) -> Self {
26 Self { entries: Vec::with_capacity(capacity), free: Vec::new(), map: HashMap::with_capacity(capacity) }
27 }
28
29 fn get(&self, key: &K) -> Option<&V> {
30 self.map.get(key).and_then(|&slot| self.entries[slot].as_ref()).map(|(_, v)| v)
31 }
32
33 fn insert(&mut self, key: K, value: V) -> Option<V> {
35 if let Some(&slot) = self.map.get(&key) {
36 return self.entries[slot].replace((key, value)).map(|(_, old)| old);
37 }
38 let slot = match self.free.pop() {
39 Some(slot) => {
40 self.entries[slot] = Some((key.clone(), value));
41 slot
42 }
43 None => {
44 self.entries.push(Some((key.clone(), value)));
45 self.entries.len() - 1
46 }
47 };
48 self.map.insert(key, slot);
49 None
50 }
51
52 fn remove(&mut self, key: &K) -> Option<V> {
53 let slot = self.map.remove(key)?;
54 self.free.push(slot);
55 self.entries[slot].take().map(|(_, v)| v)
56 }
57
58 fn to_vec(&self) -> Vec<(K, V)> {
59 self.entries.iter().flatten().cloned().collect()
60 }
61}
62
63pub struct FerroDictionary<K, V>(Rc<FerroDictionaryData<K, V>>);
75
76struct FerroDictionaryData<K, V> {
77 inner: RefCell<Inner<K, V>>,
78 collection_changed: HandlerList<CollectionChangedHandler<(K, V)>>,
79 untyped_collection_changed: Event<CollectionChange>,
84 property_changed: Event<str>,
85}
86
87pub struct WeakFerroDictionary<K, V>(Weak<FerroDictionaryData<K, V>>);
89
90impl<K, V> Clone for WeakFerroDictionary<K, V> {
91 fn clone(&self) -> Self {
92 Self(self.0.clone())
93 }
94}
95
96impl<K: 'static, V: 'static> WeakEventSender for FerroDictionary<K, V> {
97 type Weak = WeakFerroDictionary<K, V>;
98
99 fn downgrade_sender(&self) -> WeakFerroDictionary<K, V> {
100 WeakFerroDictionary(Rc::downgrade(&self.0))
101 }
102
103 fn upgrade_sender(weak: &WeakFerroDictionary<K, V>) -> Option<Self> {
104 weak.0.upgrade().map(FerroDictionary)
105 }
106
107 fn sender_address(&self) -> usize {
108 Rc::as_ptr(&self.0) as *const () as usize
109 }
110}
111
112impl<K, V> Clone for FerroDictionary<K, V> {
113 #[inline]
114 fn clone(&self) -> Self {
115 Self(self.0.clone())
116 }
117}
118
119impl<K, V> PartialEq for FerroDictionary<K, V> {
120 #[inline]
121 fn eq(&self, other: &Self) -> bool {
122 self.ptr_eq(other)
123 }
124}
125
126impl<K, V> FerroDictionary<K, V> {
127 #[inline]
129 pub fn ptr_eq(&self, other: &Self) -> bool {
130 Rc::ptr_eq(&self.0, &other.0)
131 }
132}
133
134impl<K, V> std::fmt::Debug for FerroDictionary<K, V> {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 f.write_str("FerroDictionary")
137 }
138}
139
140impl<K: Eq + Hash + Clone + Display, V: Clone> Default for FerroDictionary<K, V> {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146impl<K: Eq + Hash + Clone + Display, V: Clone> FerroDictionary<K, V> {
147 pub fn new() -> Self {
149 Self::with_capacity(0)
150 }
151
152 pub fn with_capacity(capacity: usize) -> Self {
154 Self(Rc::new(FerroDictionaryData {
155 inner: RefCell::new(Inner::with_capacity(capacity)),
156 collection_changed: HandlerList::with_ordered_ids(),
157 untyped_collection_changed: Event::with_ordered_ids(),
158 property_changed: Event::new(),
159 }))
160 }
161
162 pub fn from_dictionary(dictionary: impl IntoIterator<Item = (K, V)>) -> Self {
165 let this = Self::new();
166 {
167 let mut inner = this.0.inner.borrow_mut();
168 for (key, value) in dictionary {
169 inner.insert(key, value);
170 }
171 }
172 this
173 }
174
175 pub fn add_collection_changed(&self, handler: Rc<CollectionChangedHandler<(K, V)>>) -> u64 {
178 self.0.collection_changed.add(handler)
179 }
180
181 pub fn remove_collection_changed(&self, token: u64) -> bool {
183 self.0.collection_changed.remove(token)
184 }
185
186 pub fn has_collection_changed_subscribers(&self) -> bool {
189 !self.0.collection_changed.is_empty() || self.0.untyped_collection_changed.has_handlers()
190 }
191
192 pub fn count(&self) -> usize {
194 self.0.inner.borrow().map.len()
195 }
196
197 pub fn is_empty(&self) -> bool {
198 self.count() == 0
199 }
200
201 pub fn is_read_only(&self) -> bool {
202 false
203 }
204
205 pub fn keys(&self) -> Vec<K> {
207 self.0.inner.borrow().entries.iter().flatten().map(|(k, _)| k.clone()).collect()
208 }
209
210 pub fn values(&self) -> Vec<V> {
212 self.0.inner.borrow().entries.iter().flatten().map(|(_, v)| v.clone()).collect()
213 }
214
215 pub fn get(&self, key: &K) -> V {
217 match self.try_get_value(key) {
218 Some(value) => value,
219 None => panic!("The given key '{key}' was not present in the dictionary."),
220 }
221 }
222
223 pub fn try_get_value(&self, key: &K) -> Option<V> {
225 self.0.inner.borrow().get(key).cloned()
226 }
227
228 pub fn set(&self, key: K, value: V) {
230 let old = self.0.inner.borrow_mut().insert(key.clone(), value.clone());
231 match old {
232 Some(old) => {
233 self.0.property_changed.raise(&format!("{INDEXER_NAME}[{key}]"));
234 if self.has_collection_changed_subscribers() {
235 self.raise_collection_changed(
236 NotifyCollectionChangedAction::Replace,
237 &[(key.clone(), value)],
238 &[(key, old)],
239 );
240 }
241 }
242 None => self.notify_add(key, value),
243 }
244 }
245
246 pub fn add(&self, key: K, value: V) {
248 {
249 let mut inner = self.0.inner.borrow_mut();
250 if inner.map.contains_key(&key) {
251 drop(inner);
252 panic!("An item with the same key has already been added. Key: {key}");
253 }
254 inner.insert(key.clone(), value.clone());
255 }
256 self.notify_add(key, value);
257 }
258
259 pub fn clear(&self) {
261 let old = std::mem::replace(&mut *self.0.inner.borrow_mut(), Inner::with_capacity(0));
262
263 self.0.property_changed.raise("Count");
264 self.0.property_changed.raise(INDEXER_NAME);
265
266 if self.has_collection_changed_subscribers() {
267 self.raise_collection_changed(NotifyCollectionChangedAction::Remove, &[], &old.to_vec());
268 }
269 }
270
271 pub fn contains_key(&self, key: &K) -> bool {
272 self.0.inner.borrow().map.contains_key(key)
273 }
274
275 pub fn contains(&self, key: &K, value: &V) -> bool
278 where
279 V: PartialEq,
280 {
281 self.0.inner.borrow().get(key).is_some_and(|v| v == value)
282 }
283
284 pub fn to_vec(&self) -> Vec<(K, V)> {
286 self.0.inner.borrow().to_vec()
287 }
288
289 pub fn copy_to(&self, array: &mut [(K, V)], array_index: usize) {
295 let entries = self.to_vec();
296 assert!(
297 array_index <= array.len() && array.len() - array_index >= entries.len(),
298 "Destination array is not long enough to copy all the items in the collection. Check array index and length."
299 );
300 array[array_index..array_index + entries.len()].clone_from_slice(&entries);
301 }
302
303 pub fn for_each(&self, mut f: impl FnMut(&K, &V)) {
305 for (key, value) in self.to_vec() {
306 f(&key, &value);
307 }
308 }
309
310 pub fn remove(&self, key: &K) -> bool {
312 let removed = self.0.inner.borrow_mut().remove(key);
313 match removed {
314 Some(value) => {
315 self.0.property_changed.raise("Count");
316 self.0.property_changed.raise(&format!("{INDEXER_NAME}[{key}]"));
317
318 if self.has_collection_changed_subscribers() {
319 self.raise_collection_changed(NotifyCollectionChangedAction::Remove, &[], &[(key.clone(), value)]);
320 }
321 true
322 }
323 None => false,
324 }
325 }
326
327 fn notify_add(&self, key: K, value: V) {
328 self.0.property_changed.raise("Count");
329 self.0.property_changed.raise(&format!("{INDEXER_NAME}[{key}]"));
330
331 if self.has_collection_changed_subscribers() {
332 self.raise_collection_changed(NotifyCollectionChangedAction::Add, &[(key, value)], &[]);
333 }
334 }
335
336 fn raise_collection_changed(&self, action: NotifyCollectionChangedAction, new_items: &[(K, V)], old_items: &[(K, V)]) {
337 let e = NotifyCollectionChangedEventArgs { action, new_items, old_items, new_starting_index: -1, old_starting_index: -1 };
339 raise_collection_changed(&self.0.collection_changed, &self.0.untyped_collection_changed, &e);
340 }
341}
342
343impl<K, V> INotifyCollectionChanged for FerroDictionary<K, V> {
344 fn collection_changed(&self) -> &Event<CollectionChange> {
345 &self.0.untyped_collection_changed
346 }
347}
348
349impl<K: Eq + Hash + Clone + Display, V: Clone> INotifyPropertyChanged for FerroDictionary<K, V> {
350 fn property_changed(&self) -> &Event<str> {
351 &self.0.property_changed
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358 use std::cell::RefCell;
359
360 type Args = (NotifyCollectionChangedAction, Vec<(String, String)>, Vec<(String, String)>, i32, i32);
361
362 struct CollectionChangedTracker {
363 args: Rc<RefCell<Option<Args>>>,
364 }
365
366 impl CollectionChangedTracker {
367 fn new(target: &FerroDictionary<String, String>) -> Self {
368 let args = Rc::new(RefCell::new(None));
369 let sink = args.clone();
370 target.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, (String, String)>| {
371 *sink.borrow_mut() =
372 Some((e.action, e.new_items.to_vec(), e.old_items.to_vec(), e.new_starting_index, e.old_starting_index));
373 }));
374 Self { args }
375 }
376
377 fn args(&self) -> Args {
378 self.args.borrow().clone().expect("a collection changed notification")
379 }
380 }
381
382 struct PropertyChangedTracker {
383 names: Rc<RefCell<Vec<String>>>,
384 }
385
386 impl PropertyChangedTracker {
387 fn new(target: &FerroDictionary<String, String>) -> Self {
388 let names = Rc::new(RefCell::new(Vec::new()));
389 let sink = names.clone();
390 target.property_changed().add(Rc::new(move |name: &str| sink.borrow_mut().push(name.to_string())));
391 Self { names }
392 }
393
394 fn names(&self) -> Vec<String> {
395 self.names.borrow().clone()
396 }
397 }
398
399 fn s(value: &str) -> String {
400 value.to_string()
401 }
402
403 fn pair(key: &str, value: &str) -> (String, String) {
404 (s(key), s(value))
405 }
406
407 #[test]
408 fn adding_item_should_raise_collection_changed() {
409 let target = FerroDictionary::<String, String>::new();
410 let tracker = CollectionChangedTracker::new(&target);
411
412 target.add(s("foo"), s("bar"));
413
414 let (action, new_items, _, new_starting_index, _) = tracker.args();
415 assert_eq!(NotifyCollectionChangedAction::Add, action);
416 assert_eq!(-1, new_starting_index);
417 assert_eq!(1, new_items.len());
418 assert_eq!(pair("foo", "bar"), new_items[0]);
419 }
420
421 #[test]
422 fn adding_item_should_raise_property_changed() {
423 let target = FerroDictionary::<String, String>::new();
424 let tracker = PropertyChangedTracker::new(&target);
425
426 target.add(s("foo"), s("bar"));
427
428 assert_eq!(vec!["Count", "Item[foo]"], tracker.names());
429 }
430
431 #[test]
432 fn assigning_item_should_raise_collection_changed_add() {
433 let target = FerroDictionary::<String, String>::new();
434 let tracker = CollectionChangedTracker::new(&target);
435
436 target.set(s("foo"), s("bar"));
437
438 let (action, new_items, _, new_starting_index, _) = tracker.args();
439 assert_eq!(NotifyCollectionChangedAction::Add, action);
440 assert_eq!(-1, new_starting_index);
441 assert_eq!(1, new_items.len());
442 assert_eq!(pair("foo", "bar"), new_items[0]);
443 }
444
445 #[test]
446 fn assigning_item_should_raise_collection_changed_replace() {
447 let target = FerroDictionary::<String, String>::new();
448
449 target.set(s("foo"), s("baz"));
450 let tracker = CollectionChangedTracker::new(&target);
451 target.set(s("foo"), s("bar"));
452
453 let (action, new_items, _, new_starting_index, _) = tracker.args();
454 assert_eq!(NotifyCollectionChangedAction::Replace, action);
455 assert_eq!(-1, new_starting_index);
456 assert_eq!(1, new_items.len());
457 assert_eq!(pair("foo", "bar"), new_items[0]);
458 }
459
460 #[test]
461 fn assigning_item_should_raise_property_changed_add() {
462 let target = FerroDictionary::<String, String>::new();
463 let tracker = PropertyChangedTracker::new(&target);
464
465 target.set(s("foo"), s("bar"));
466
467 assert_eq!(vec!["Count", "Item[foo]"], tracker.names());
468 }
469
470 #[test]
471 fn assigning_item_should_raise_property_changed_replace() {
472 let target = FerroDictionary::<String, String>::new();
473
474 target.set(s("foo"), s("baz"));
475 let tracker = PropertyChangedTracker::new(&target);
476 target.set(s("foo"), s("bar"));
477
478 assert_eq!(vec!["Item[foo]"], tracker.names());
479 }
480
481 #[test]
482 fn removing_item_should_raise_collection_changed() {
483 let target = FerroDictionary::<String, String>::new();
484
485 target.set(s("foo"), s("bar"));
486 let tracker = CollectionChangedTracker::new(&target);
487 target.remove(&s("foo"));
488
489 let (action, _, old_items, _, old_starting_index) = tracker.args();
490 assert_eq!(NotifyCollectionChangedAction::Remove, action);
491 assert_eq!(-1, old_starting_index);
492 assert_eq!(1, old_items.len());
493 assert_eq!(pair("foo", "bar"), old_items[0]);
494 }
495
496 #[test]
497 fn remove_method_should_remove_item_from_collection() {
498 let target = FerroDictionary::from_dictionary([pair("foo", "bar")]);
499 assert_eq!(target.count(), 1);
500
501 target.remove(&s("foo"));
502 assert_eq!(target.count(), 0);
503 }
504
505 #[test]
506 fn removing_item_should_raise_property_changed() {
507 let target = FerroDictionary::<String, String>::new();
508
509 target.set(s("foo"), s("bar"));
510 let tracker = PropertyChangedTracker::new(&target);
511 target.remove(&s("foo"));
512
513 assert_eq!(vec!["Count", "Item[foo]"], tracker.names());
514 }
515
516 #[test]
517 fn clearing_collection_should_raise_collection_changed() {
518 let target = FerroDictionary::<String, String>::new();
519
520 target.set(s("foo"), s("bar"));
521 target.set(s("baz"), s("qux"));
522 let tracker = CollectionChangedTracker::new(&target);
523 target.clear();
524
525 let (action, _, old_items, _, old_starting_index) = tracker.args();
526 assert_eq!(NotifyCollectionChangedAction::Remove, action);
527 assert_eq!(-1, old_starting_index);
528 assert_eq!(2, old_items.len());
529 assert_eq!(pair("foo", "bar"), old_items[0]);
530 }
531
532 #[test]
533 fn clearing_collection_should_raise_property_changed() {
534 let target = FerroDictionary::<String, String>::new();
535
536 target.set(s("foo"), s("bar"));
537 target.set(s("baz"), s("qux"));
538 let tracker = PropertyChangedTracker::new(&target);
539 target.clear();
540
541 assert_eq!(vec!["Count", INDEXER_NAME], tracker.names());
542 }
543
544 #[test]
545 fn constructor_should_initialize_with_provided_collection() {
546 let initial_collection = HashMap::from([pair("key1", "value1"), pair("key2", "value2")]);
547
548 let target = FerroDictionary::from_dictionary(initial_collection);
549
550 assert_eq!(2, target.count());
551 assert_eq!("value1", target.get(&s("key1")));
552 assert_eq!("value2", target.get(&s("key2")));
553 }
554
555 #[test]
557 fn enumeration_follows_insertion_and_reuses_removed_slots() {
558 let target = FerroDictionary::<String, i32>::new();
559 target.add(s("a"), 1);
560 target.add(s("b"), 2);
561 target.add(s("c"), 3);
562 assert_eq!(vec![s("a"), s("b"), s("c")], target.keys());
563
564 target.remove(&s("b"));
565 target.add(s("d"), 4);
566 assert_eq!(vec![s("a"), s("d"), s("c")], target.keys());
567 assert_eq!(vec![1, 4, 3], target.values());
568 assert!(target.contains_key(&s("d")));
569 assert!(!target.contains_key(&s("b")));
570 assert!(target.contains(&s("d"), &4));
571 assert_eq!(None, target.try_get_value(&s("b")));
572 }
573
574 #[test]
576 fn copy_to_copies_the_entries_in_enumeration_order() {
577 let target = FerroDictionary::<String, i32>::new();
578 target.add(s("a"), 1);
579 target.add(s("b"), 2);
580 let mut array = vec![(s(""), 0); 3];
581
582 target.copy_to(&mut array, 1);
583
584 assert_eq!(vec![(s(""), 0), (s("a"), 1), (s("b"), 2)], array);
585 }
586
587 #[test]
589 #[should_panic(expected = "Destination array is not long enough")]
590 fn copy_to_panics_when_the_array_is_too_small() {
591 let target = FerroDictionary::<String, i32>::new();
592 target.add(s("a"), 1);
593 target.add(s("b"), 2);
594 let mut array = vec![(s(""), 0); 2];
595
596 target.copy_to(&mut array, 1);
597 }
598}