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