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}