Skip to main content

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 {}