ferroui_reactiveui_testing/i_builder_extensions.rs
1//! `IBuilderExtensions` (`ReactiveUI.Testing/IBuilderExtensions.cs`).
2
3use crate::IBuilder;
4use std::collections::HashMap;
5use std::hash::Hash;
6
7/// The fluent setters of a builder of test data (`IBuilderExtensions`): each sets or fills a field
8/// of the builder and returns the builder.
9///
10/// Deviation (DEVIATIONS.md, ReactiveUI): upstream passes the field by reference (`out` or `ref`)
11/// next to the builder, which is a reference type; a Rust builder is moved through the chain, so
12/// the field is named by an accessor, a closure that gives the field of the builder:
13///
14/// ```
15/// use ferroui_reactiveui_testing::{IBuilder, IBuilderExtensions};
16///
17/// #[derive(Default)]
18/// struct PersonBuilder {
19/// name: String,
20/// tags: Vec<String>,
21/// }
22///
23/// impl IBuilder for PersonBuilder {}
24///
25/// impl PersonBuilder {
26/// fn with_name(self, name: &str) -> Self {
27/// self.with(|builder| &mut builder.name, name.to_string())
28/// }
29///
30/// fn with_tag(self, tag: &str) -> Self {
31/// self.with_value(|builder| &mut builder.tags, tag.to_string())
32/// }
33/// }
34///
35/// let builder = PersonBuilder::default().with_name("Ada").with_tag("first");
36/// assert_eq!(builder.name, "Ada");
37/// assert_eq!(builder.tags, ["first"]);
38/// ```
39///
40/// The overloads get names of their own: `with` (`With<TField>(out TField, TField)`),
41/// `with_values` and `with_value` for a list, `with_pair`, `with_key_value` and `with_dictionary`
42/// for a dictionary. Upstream declares the last one for every type, not only builders; here it
43/// is a method of builders as the others are.
44///
45/// Not ported: the `ArgumentException` of a list or dictionary field that is null - argument
46/// validation (the fields cannot be null).
47pub trait IBuilderExtensions: IBuilder + Sized {
48 /// Sets the field to `value` (`With<TField>(out TField field, TField value)`).
49 fn with<F>(mut self, field: impl FnOnce(&mut Self) -> &mut F, value: F) -> Self {
50 *field(&mut self) = value;
51 self
52 }
53
54 /// Adds `values` to the list (`With<TField>(ref List<TField>? field, IEnumerable<TField>
55 /// values)`).
56 fn with_values<F>(mut self, field: impl FnOnce(&mut Self) -> &mut Vec<F>, values: impl IntoIterator<Item = F>) -> Self {
57 field(&mut self).extend(values);
58 self
59 }
60
61 /// Adds `value` to the list (`With<TField>(ref List<TField>? field, TField value)`).
62 fn with_value<F>(mut self, field: impl FnOnce(&mut Self) -> &mut Vec<F>, value: F) -> Self {
63 field(&mut self).push(value);
64 self
65 }
66
67 /// Adds the key and value of `key_value_pair` to the dictionary
68 /// (`With<TKey, TField>(ref Dictionary<TKey, TField>, KeyValuePair<TKey, TField>)`).
69 ///
70 /// # Panics
71 /// When the dictionary holds the key, as `Dictionary.Add` throws.
72 fn with_pair<K: Eq + Hash, F>(self, dictionary: impl FnOnce(&mut Self) -> &mut HashMap<K, F>, key_value_pair: (K, F)) -> Self {
73 let (key, value) = key_value_pair;
74 self.with_key_value(dictionary, key, value)
75 }
76
77 /// Adds `key` with `value` to the dictionary
78 /// (`With<TKey, TField>(ref Dictionary<TKey, TField>, TKey, TField)`).
79 ///
80 /// # Panics
81 /// When the dictionary holds the key, as `Dictionary.Add` throws.
82 fn with_key_value<K: Eq + Hash, F>(mut self, dictionary: impl FnOnce(&mut Self) -> &mut HashMap<K, F>, key: K, value: F) -> Self {
83 let dictionary = dictionary(&mut self);
84 assert!(!dictionary.contains_key(&key), "An item with the same key has already been added.");
85 dictionary.insert(key, value);
86 self
87 }
88
89 /// Replaces the dictionary with a copy of `values`
90 /// (`With<TKey, TField>(ref Dictionary<TKey, TField>, IDictionary<TKey, TField>)`).
91 fn with_dictionary<K: Eq + Hash + Clone, F: Clone>(mut self, dictionary: impl FnOnce(&mut Self) -> &mut HashMap<K, F>, values: &HashMap<K, F>) -> Self {
92 *dictionary(&mut self) = values.clone();
93 self
94 }
95}
96
97impl<B: IBuilder> IBuilderExtensions for B {}