known_values/
known_value_store.rs

1use std::collections::HashMap;
2#[cfg(feature = "directory-loading")]
3use std::path::Path;
4
5use super::known_value::KnownValue;
6
7/// A store that maps between Known Values and their assigned names.
8///
9/// The `KnownValuesStore` provides a bidirectional mapping between:
10/// - Numeric values (u64) and their corresponding KnownValue instances
11/// - String names and their corresponding KnownValue instances
12///
13/// This enables efficient lookup in both directions, making it possible to:
14/// - Find the name for a given numeric value
15/// - Find the numeric value for a given name
16/// - Retrieve complete KnownValue instances by either name or value
17///
18/// The store is typically populated with predefined Known Values from the
19/// registry, but can also be extended with custom values.
20///
21/// # Examples
22///
23/// ```
24/// use std::collections::HashMap;
25///
26/// use known_values::{KnownValue, KnownValuesStore};
27///
28/// // Create a store with predefined Known Values
29/// let store = KnownValuesStore::new([
30///     known_values::IS_A,
31///     known_values::NOTE,
32///     known_values::SIGNED,
33/// ]);
34///
35/// // Look up a Known Value by name
36/// let is_a = store.known_value_named("isA").unwrap();
37/// assert_eq!(is_a.value(), 1);
38///
39/// // Look up a name for a raw value
40/// let name = store.name(KnownValue::new(3));
41/// assert_eq!(name, "signed");
42///
43/// // Insert a custom Known Value
44/// let mut custom_store = store.clone();
45/// custom_store
46///     .insert(KnownValue::new_with_name(100u64, "customValue".to_string()));
47/// assert_eq!(
48///     custom_store
49///         .known_value_named("customValue")
50///         .unwrap()
51///         .value(),
52///     100
53/// );
54/// ```
55#[derive(Clone, Debug)]
56pub struct KnownValuesStore {
57    known_values_by_raw_value: HashMap<u64, KnownValue>,
58    known_values_by_assigned_name: HashMap<String, KnownValue>,
59}
60
61impl KnownValuesStore {
62    /// Creates a new KnownValuesStore with the provided Known Values.
63    ///
64    /// This constructor takes any iterable of KnownValue instances and
65    /// populates the store with them, creating mappings from both raw
66    /// values and names to the corresponding KnownValue instances.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use known_values::KnownValuesStore;
72    ///
73    /// // Create a store with predefined Known Values
74    /// let store = KnownValuesStore::new([
75    ///     known_values::IS_A,
76    ///     known_values::NOTE,
77    ///     known_values::SIGNED,
78    /// ]);
79    ///
80    /// // Look up Known Values
81    /// assert_eq!(store.known_value_named("isA").unwrap().value(), 1);
82    /// assert_eq!(store.known_value_named("note").unwrap().value(), 4);
83    /// ```
84    pub fn new<T>(known_values: T) -> Self
85    where
86        T: IntoIterator<Item = KnownValue>,
87    {
88        let mut known_values_by_raw_value = HashMap::new();
89        let mut known_values_by_assigned_name = HashMap::new();
90        for known_value in known_values {
91            Self::_insert(
92                known_value,
93                &mut known_values_by_raw_value,
94                &mut known_values_by_assigned_name,
95            );
96        }
97        Self {
98            known_values_by_raw_value,
99            known_values_by_assigned_name,
100        }
101    }
102
103    /// Inserts a KnownValue into the store.
104    ///
105    /// If the KnownValue has an assigned name, it will be indexed by both its
106    /// raw value and its name. If a KnownValue with the same raw value or name
107    /// already exists in the store, it will be replaced.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use known_values::{KnownValue, KnownValuesStore};
113    ///
114    /// let mut store = KnownValuesStore::default();
115    /// store.insert(KnownValue::new_with_name(100u64, "customValue".to_string()));
116    /// assert_eq!(store.known_value_named("customValue").unwrap().value(), 100);
117    /// ```
118    pub fn insert(&mut self, known_value: KnownValue) {
119        Self::_insert(
120            known_value,
121            &mut self.known_values_by_raw_value,
122            &mut self.known_values_by_assigned_name,
123        );
124    }
125
126    /// Returns the assigned name for a KnownValue, if present in the store.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// use known_values::{KnownValue, KnownValuesStore};
132    ///
133    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
134    ///
135    /// assert_eq!(store.assigned_name(&known_values::IS_A), Some("isA"));
136    /// assert_eq!(store.assigned_name(&KnownValue::new(999)), None);
137    /// ```
138    pub fn assigned_name(&self, known_value: &KnownValue) -> Option<&str> {
139        self.known_values_by_raw_value
140            .get(&known_value.value())
141            .and_then(|known_value| known_value.assigned_name())
142    }
143
144    /// Returns a human-readable name for a KnownValue.
145    ///
146    /// If the KnownValue has an assigned name in the store, that name is
147    /// returned. Otherwise, the KnownValue's default name (which may be its
148    /// numeric value as a string) is returned.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use known_values::{KnownValue, KnownValuesStore};
154    ///
155    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
156    ///
157    /// assert_eq!(store.name(known_values::IS_A), "isA");
158    /// assert_eq!(store.name(KnownValue::new(999)), "999");
159    /// ```
160    pub fn name(&self, known_value: KnownValue) -> String {
161        self.assigned_name(&known_value)
162            .map(|name| name.to_string())
163            .unwrap_or_else(|| known_value.name())
164    }
165
166    /// Looks up a KnownValue by its assigned name.
167    ///
168    /// Returns a reference to the KnownValue if found, or None if no KnownValue
169    /// with the given name exists in the store.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use known_values::KnownValuesStore;
175    ///
176    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
177    ///
178    /// let is_a = store.known_value_named("isA").unwrap();
179    /// assert_eq!(is_a.value(), 1);
180    ///
181    /// assert!(store.known_value_named("nonexistent").is_none());
182    /// ```
183    pub fn known_value_named(
184        &self,
185        assigned_name: &str,
186    ) -> Option<&KnownValue> {
187        self.known_values_by_assigned_name.get(assigned_name)
188    }
189
190    /// Retrieves a KnownValue for a raw value, using a store if provided.
191    ///
192    /// This static method allows looking up a KnownValue by its raw numeric
193    /// value:
194    /// - If a store is provided and contains a mapping for the raw value, that
195    ///   KnownValue is returned
196    /// - Otherwise, a new KnownValue with no assigned name is created and
197    ///   returned
198    ///
199    /// # Examples
200    ///
201    /// ```
202    /// use known_values::KnownValuesStore;
203    ///
204    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
205    ///
206    /// // Known value from store
207    /// let is_a = KnownValuesStore::known_value_for_raw_value(1, Some(&store));
208    /// assert_eq!(is_a.name(), "isA");
209    ///
210    /// // Unknown value creates a new KnownValue
211    /// let unknown =
212    ///     KnownValuesStore::known_value_for_raw_value(999, Some(&store));
213    /// assert_eq!(unknown.name(), "999");
214    ///
215    /// // No store provided also creates a new KnownValue
216    /// let unknown = KnownValuesStore::known_value_for_raw_value(1, None);
217    /// assert_eq!(unknown.name(), "1");
218    /// ```
219    pub fn known_value_for_raw_value(
220        raw_value: u64,
221        known_values: Option<&Self>,
222    ) -> KnownValue {
223        known_values
224            .and_then(|known_values| {
225                known_values.known_values_by_raw_value.get(&raw_value)
226            })
227            .cloned()
228            .unwrap_or_else(|| KnownValue::new(raw_value))
229    }
230
231    /// Attempts to find a KnownValue by its name, using a store if provided.
232    ///
233    /// This static method allows looking up a KnownValue by its name:
234    /// - If a store is provided and contains a mapping for the name, that
235    ///   KnownValue is returned
236    /// - Otherwise, None is returned
237    ///
238    /// # Examples
239    ///
240    /// ```
241    /// use known_values::KnownValuesStore;
242    ///
243    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
244    ///
245    /// // Known value from store
246    /// let is_a = KnownValuesStore::known_value_for_name("isA", Some(&store));
247    /// assert_eq!(is_a.unwrap().value(), 1);
248    ///
249    /// // Unknown name returns None
250    /// assert!(
251    ///     KnownValuesStore::known_value_for_name("unknown", Some(&store))
252    ///         .is_none()
253    /// );
254    ///
255    /// // No store provided also returns None
256    /// assert!(KnownValuesStore::known_value_for_name("isA", None).is_none());
257    /// ```
258    pub fn known_value_for_name(
259        name: &str,
260        known_values: Option<&Self>,
261    ) -> Option<KnownValue> {
262        known_values
263            .and_then(|known_values| known_values.known_value_named(name))
264            .cloned()
265    }
266
267    /// Returns a human-readable name for a KnownValue, using a store if
268    /// provided.
269    ///
270    /// This static method allows getting a name for a KnownValue:
271    /// - If a store is provided and contains a mapping for the KnownValue, its
272    ///   assigned name is returned
273    /// - Otherwise, the KnownValue's default name (which may be its numeric
274    ///   value as a string) is returned
275    ///
276    /// # Examples
277    ///
278    /// ```
279    /// use known_values::{KnownValue, KnownValuesStore};
280    ///
281    /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
282    ///
283    /// // Known value from store
284    /// let name = KnownValuesStore::name_for_known_value(
285    ///     known_values::IS_A,
286    ///     Some(&store),
287    /// );
288    /// assert_eq!(name, "isA");
289    ///
290    /// // Unknown value in store uses KnownValue's name method
291    /// let name = KnownValuesStore::name_for_known_value(
292    ///     KnownValue::new(999),
293    ///     Some(&store),
294    /// );
295    /// assert_eq!(name, "999");
296    ///
297    /// // No store provided also uses KnownValue's name method
298    /// let name = KnownValuesStore::name_for_known_value(known_values::IS_A, None);
299    /// assert_eq!(name, "isA");
300    /// ```
301    pub fn name_for_known_value(
302        known_value: KnownValue,
303        known_values: Option<&Self>,
304    ) -> String {
305        known_values
306            .and_then(|known_values| known_values.assigned_name(&known_value))
307            .map(|assigned_name| assigned_name.to_string())
308            .unwrap_or_else(|| known_value.name())
309    }
310
311    /// Internal helper method to insert a KnownValue into the store's maps.
312    ///
313    /// When inserting a value with a codepoint that already exists, this method
314    /// removes the old name from the name index before adding the new one.
315    fn _insert(
316        known_value: KnownValue,
317        known_values_by_raw_value: &mut HashMap<u64, KnownValue>,
318        known_values_by_assigned_name: &mut HashMap<String, KnownValue>,
319    ) {
320        // If there's an existing value with the same codepoint, remove its name
321        // from the name index to avoid stale entries
322        if let Some(old_value) =
323            known_values_by_raw_value.get(&known_value.value())
324            && let Some(old_name) = old_value.assigned_name()
325        {
326            known_values_by_assigned_name.remove(old_name);
327        }
328
329        known_values_by_raw_value
330            .insert(known_value.value(), known_value.clone());
331        if let Some(name) = known_value.assigned_name() {
332            known_values_by_assigned_name.insert(name.to_string(), known_value);
333        }
334    }
335
336    /// Loads and inserts known values from a directory containing JSON registry
337    /// files.
338    ///
339    /// This method scans the specified directory for `.json` files and parses
340    /// them as known value registries. Values from JSON files override
341    /// existing values in the store when codepoints match.
342    ///
343    /// This method is only available when the `directory-loading` feature is
344    /// enabled.
345    ///
346    /// # Arguments
347    ///
348    /// * `path` - The directory to scan for JSON registry files.
349    ///
350    /// # Returns
351    ///
352    /// Returns `Ok(count)` with the number of values loaded, or an error if
353    /// directory traversal fails.
354    ///
355    /// # Examples
356    ///
357    /// ```rust,ignore
358    /// use known_values::KnownValuesStore;
359    /// use std::path::Path;
360    ///
361    /// let mut store = KnownValuesStore::default();
362    /// let count = store.load_from_directory(Path::new("/etc/known-values"))?;
363    /// println!("Loaded {} values", count);
364    /// ```
365    #[cfg(feature = "directory-loading")]
366    pub fn load_from_directory(
367        &mut self,
368        path: &Path,
369    ) -> Result<usize, crate::LoadError> {
370        let values = crate::directory_loader::load_from_directory(path)?;
371        let count = values.len();
372        for value in values {
373            self.insert(value);
374        }
375        Ok(count)
376    }
377
378    /// Loads known values from multiple directories using the provided
379    /// configuration.
380    ///
381    /// Directories are processed in order. When multiple entries have the same
382    /// codepoint, values from later directories override values from earlier
383    /// directories.
384    ///
385    /// This method is only available when the `directory-loading` feature is
386    /// enabled.
387    ///
388    /// # Arguments
389    ///
390    /// * `config` - The directory configuration specifying search paths.
391    ///
392    /// # Returns
393    ///
394    /// A `LoadResult` containing information about the loading operation.
395    ///
396    /// # Examples
397    ///
398    /// ```rust,ignore
399    /// use known_values::{KnownValuesStore, DirectoryConfig};
400    ///
401    /// let mut store = KnownValuesStore::default();
402    /// let config = DirectoryConfig::default_only();
403    /// let result = store.load_from_config(&config);
404    ///
405    /// println!("Loaded {} values", result.values_count());
406    /// if result.has_errors() {
407    ///     for (path, error) in &result.errors {
408    ///         eprintln!("Error: {}: {}", path.display(), error);
409    ///     }
410    /// }
411    /// ```
412    #[cfg(feature = "directory-loading")]
413    pub fn load_from_config(
414        &mut self,
415        config: &crate::DirectoryConfig,
416    ) -> crate::LoadResult {
417        let result = crate::directory_loader::load_from_config(config);
418        for value in result.values.values() {
419            self.insert(value.clone());
420        }
421        result
422    }
423}
424
425/// Default implementation creates an empty KnownValuesStore.
426impl Default for KnownValuesStore {
427    fn default() -> Self { Self::new([]) }
428}