known_values/
known_value_store.rs

1use std::collections::HashMap;
2
3use super::known_value::KnownValue;
4
5/// A store that maps between Known Values and their assigned names.
6///
7/// The `KnownValuesStore` provides a bidirectional mapping between:
8/// - Numeric values (u64) and their corresponding KnownValue instances
9/// - String names and their corresponding KnownValue instances
10///
11/// This enables efficient lookup in both directions, making it possible to:
12/// - Find the name for a given numeric value
13/// - Find the numeric value for a given name
14/// - Retrieve complete KnownValue instances by either name or value
15///
16/// The store is typically populated with predefined Known Values from the
17/// registry, but can also be extended with custom values.
18///
19/// # Examples
20///
21/// ```
22/// use std::collections::HashMap;
23///
24/// use known_values::{KnownValue, KnownValuesStore};
25///
26/// // Create a store with predefined Known Values
27/// let store = KnownValuesStore::new([
28///     known_values::IS_A,
29///     known_values::NOTE,
30///     known_values::SIGNED,
31/// ]);
32///
33/// // Look up a Known Value by name
34/// let is_a = store.known_value_named("isA").unwrap();
35/// assert_eq!(is_a.value(), 1);
36///
37/// // Look up a name for a raw value
38/// let name = store.name(KnownValue::new(3));
39/// assert_eq!(name, "signed");
40///
41/// // Insert a custom Known Value
42/// let mut custom_store = store.clone();
43/// custom_store
44///     .insert(KnownValue::new_with_name(100u64, "customValue".to_string()));
45/// assert_eq!(
46///     custom_store
47///         .known_value_named("customValue")
48///         .unwrap()
49///         .value(),
50///     100
51/// );
52/// ```
53#[derive(Clone, Debug)]
54pub struct KnownValuesStore {
55    known_values_by_raw_value: HashMap<u64, KnownValue>,
56    known_values_by_assigned_name: HashMap<String, KnownValue>,
57}
58
59impl KnownValuesStore {
60    /// Creates a new KnownValuesStore with the provided Known Values.
61    ///
62    /// This constructor takes any iterable of KnownValue instances and
63    /// populates the store with them, creating mappings from both raw
64    /// values and names to the corresponding KnownValue instances.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use known_values::KnownValuesStore;
70    ///
71    /// // Create a store with predefined Known Values
72    /// let store = KnownValuesStore::new([
73    ///     known_values::IS_A,
74    ///     known_values::NOTE,
75    ///     known_values::SIGNED,
76    /// ]);
77    ///
78    /// // Look up Known Values
79    /// assert_eq!(store.known_value_named("isA").unwrap().value(), 1);
80    /// assert_eq!(store.known_value_named("note").unwrap().value(), 4);
81    /// ```
82    pub fn new<T>(known_values: T) -> Self
83    where
84        T: IntoIterator<Item = KnownValue>,
85    {
86        let mut known_values_by_raw_value = HashMap::new();
87        let mut known_values_by_assigned_name = HashMap::new();
88        for known_value in known_values {
89            Self::_insert(
90                known_value,
91                &mut known_values_by_raw_value,
92                &mut known_values_by_assigned_name,
93            );
94        }
95        Self {
96            known_values_by_raw_value,
97            known_values_by_assigned_name,
98        }
99    }
100
101    /// Inserts a KnownValue into the store.
102    ///
103    /// If the KnownValue has an assigned name, it will be indexed by both its
104    /// raw value and its name. If a KnownValue with the same raw value or name
105    /// already exists in the store, it will be replaced.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use known_values::{KnownValue, KnownValuesStore};
111    ///
112    /// let mut store = KnownValuesStore::default();
113    /// store.insert(KnownValue::new_with_name(100u64, "customValue".to_string()));
114    /// assert_eq!(store.known_value_named("customValue").unwrap().value(), 100);
115    /// ```
116    pub fn insert(&mut self, known_value: KnownValue) {
117        Self::_insert(
118            known_value,
119            &mut self.known_values_by_raw_value,
120            &mut self.known_values_by_assigned_name,
121        );
122    }
123
124    /// Returns the assigned name for a KnownValue, if present in the store.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use known_values::{KnownValue, KnownValuesStore};
130    ///
131    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
132    ///
133    /// assert_eq!(store.assigned_name(&known_values::IS_A), Some("isA"));
134    /// assert_eq!(store.assigned_name(&KnownValue::new(999)), None);
135    /// ```
136    pub fn assigned_name(&self, known_value: &KnownValue) -> Option<&str> {
137        self.known_values_by_raw_value
138            .get(&known_value.value())
139            .and_then(|known_value| known_value.assigned_name())
140    }
141
142    /// Returns a human-readable name for a KnownValue.
143    ///
144    /// If the KnownValue has an assigned name in the store, that name is
145    /// returned. Otherwise, the KnownValue's default name (which may be its
146    /// numeric value as a string) is returned.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use known_values::{KnownValue, KnownValuesStore};
152    ///
153    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
154    ///
155    /// assert_eq!(store.name(known_values::IS_A), "isA");
156    /// assert_eq!(store.name(KnownValue::new(999)), "999");
157    /// ```
158    pub fn name(&self, known_value: KnownValue) -> String {
159        self.assigned_name(&known_value)
160            .map(|name| name.to_string())
161            .unwrap_or_else(|| known_value.name())
162    }
163
164    /// Looks up a KnownValue by its assigned name.
165    ///
166    /// Returns a reference to the KnownValue if found, or None if no KnownValue
167    /// with the given name exists in the store.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use known_values::KnownValuesStore;
173    ///
174    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
175    ///
176    /// let is_a = store.known_value_named("isA").unwrap();
177    /// assert_eq!(is_a.value(), 1);
178    ///
179    /// assert!(store.known_value_named("nonexistent").is_none());
180    /// ```
181    pub fn known_value_named(
182        &self,
183        assigned_name: &str,
184    ) -> Option<&KnownValue> {
185        self.known_values_by_assigned_name.get(assigned_name)
186    }
187
188    /// Retrieves a KnownValue for a raw value, using a store if provided.
189    ///
190    /// This static method allows looking up a KnownValue by its raw numeric
191    /// value:
192    /// - If a store is provided and contains a mapping for the raw value, that
193    ///   KnownValue is returned
194    /// - Otherwise, a new KnownValue with no assigned name is created and
195    ///   returned
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// use known_values::KnownValuesStore;
201    ///
202    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
203    ///
204    /// // Known value from store
205    /// let is_a = KnownValuesStore::known_value_for_raw_value(1, Some(&store));
206    /// assert_eq!(is_a.name(), "isA");
207    ///
208    /// // Unknown value creates a new KnownValue
209    /// let unknown =
210    ///     KnownValuesStore::known_value_for_raw_value(999, Some(&store));
211    /// assert_eq!(unknown.name(), "999");
212    ///
213    /// // No store provided also creates a new KnownValue
214    /// let unknown = KnownValuesStore::known_value_for_raw_value(1, None);
215    /// assert_eq!(unknown.name(), "1");
216    /// ```
217    pub fn known_value_for_raw_value(
218        raw_value: u64,
219        known_values: Option<&Self>,
220    ) -> KnownValue {
221        known_values
222            .and_then(|known_values| {
223                known_values.known_values_by_raw_value.get(&raw_value)
224            })
225            .cloned()
226            .unwrap_or_else(|| KnownValue::new(raw_value))
227    }
228
229    /// Attempts to find a KnownValue by its name, using a store if provided.
230    ///
231    /// This static method allows looking up a KnownValue by its name:
232    /// - If a store is provided and contains a mapping for the name, that
233    ///   KnownValue is returned
234    /// - Otherwise, None is returned
235    ///
236    /// # Examples
237    ///
238    /// ```
239    /// use known_values::KnownValuesStore;
240    ///
241    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
242    ///
243    /// // Known value from store
244    /// let is_a = KnownValuesStore::known_value_for_name("isA", Some(&store));
245    /// assert_eq!(is_a.unwrap().value(), 1);
246    ///
247    /// // Unknown name returns None
248    /// assert!(
249    ///     KnownValuesStore::known_value_for_name("unknown", Some(&store))
250    ///         .is_none()
251    /// );
252    ///
253    /// // No store provided also returns None
254    /// assert!(KnownValuesStore::known_value_for_name("isA", None).is_none());
255    /// ```
256    pub fn known_value_for_name(
257        name: &str,
258        known_values: Option<&Self>,
259    ) -> Option<KnownValue> {
260        known_values
261            .and_then(|known_values| known_values.known_value_named(name))
262            .cloned()
263    }
264
265    /// Returns a human-readable name for a KnownValue, using a store if
266    /// provided.
267    ///
268    /// This static method allows getting a name for a KnownValue:
269    /// - If a store is provided and contains a mapping for the KnownValue, its
270    ///   assigned name is returned
271    /// - Otherwise, the KnownValue's default name (which may be its numeric
272    ///   value as a string) is returned
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// use known_values::{KnownValue, KnownValuesStore};
278    ///
279    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
280    ///
281    /// // Known value from store
282    /// let name = KnownValuesStore::name_for_known_value(
283    ///     known_values::IS_A,
284    ///     Some(&store),
285    /// );
286    /// assert_eq!(name, "isA");
287    ///
288    /// // Unknown value in store uses KnownValue's name method
289    /// let name = KnownValuesStore::name_for_known_value(
290    ///     KnownValue::new(999),
291    ///     Some(&store),
292    /// );
293    /// assert_eq!(name, "999");
294    ///
295    /// // No store provided also uses KnownValue's name method
296    /// let name = KnownValuesStore::name_for_known_value(known_values::IS_A, None);
297    /// assert_eq!(name, "isA");
298    /// ```
299    pub fn name_for_known_value(
300        known_value: KnownValue,
301        known_values: Option<&Self>,
302    ) -> String {
303        known_values
304            .and_then(|known_values| known_values.assigned_name(&known_value))
305            .map(|assigned_name| assigned_name.to_string())
306            .unwrap_or_else(|| known_value.name())
307    }
308
309    /// Internal helper method to insert a KnownValue into the store's maps.
310    fn _insert(
311        known_value: KnownValue,
312        known_values_by_raw_value: &mut HashMap<u64, KnownValue>,
313        known_values_by_assigned_name: &mut HashMap<String, KnownValue>,
314    ) {
315        known_values_by_raw_value
316            .insert(known_value.value(), known_value.clone());
317        if let Some(name) = known_value.assigned_name() {
318            known_values_by_assigned_name.insert(name.to_string(), known_value);
319        }
320    }
321}
322
323/// Default implementation creates an empty KnownValuesStore.
324impl Default for KnownValuesStore {
325    fn default() -> Self { Self::new([]) }
326}