1use crate::context::Context;
2pub use gaze_types::{
3 DictionaryBundle, DictionaryEntry, DictionaryLoadError, DictionarySource, DictionaryStats,
4 RulepackDict,
5};
6
7pub trait DictionaryBundleExt {
8 fn from_context(ctx: &Context) -> Self;
9}
10
11impl DictionaryBundleExt for DictionaryBundle {
12 fn from_context(ctx: &Context) -> Self {
13 dictionary_bundle_from_context(ctx)
14 }
15}
16
17pub fn dictionary_bundle_from_context(ctx: &Context) -> DictionaryBundle {
18 let entries = ctx.dictionaries.iter().map(|(name, dictionary)| {
19 (
20 name.clone(),
21 DictionaryEntry::new(
22 name,
23 dictionary.terms.clone(),
24 dictionary.case_sensitive,
25 DictionarySource::Cli,
26 )
27 .expect("Context validates dictionary terms before bundle construction"),
28 )
29 });
30 DictionaryBundle::from_entries(entries)
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36 use crate::{ContextDictionary, PiiClass};
37 use serde_json::Map;
38 use std::collections::HashMap;
39
40 #[test]
41 fn context_bundle_builds_automata_per_request() {
42 let ctx = Context {
43 dictionaries: HashMap::from([(
44 "dict_alpha".to_string(),
45 ContextDictionary {
46 terms: vec!["AAA-12345".to_string()],
47 case_sensitive: true,
48 },
49 )]),
50 class_map: HashMap::from([(
51 "dict_alpha".to_string(),
52 PiiClass::Custom("class_alpha".to_string()),
53 )]),
54 fields: Map::new(),
55 };
56
57 let bundle = dictionary_bundle_from_context(&ctx);
58 let entry = bundle.get("dict_alpha").expect("entry");
59 assert_eq!(entry.terms(), &["AAA-12345".to_string()]);
60 assert!(entry.case_sensitive());
61 }
62
63 #[test]
64 fn merge_prefers_second_bundle_for_same_name() {
65 let a = DictionaryBundle::from_rulepack_terms(&[RulepackDict::new(
66 "songs",
67 vec!["Song A".to_string()],
68 true,
69 )]);
70 let b = DictionaryBundle::from_rulepack_terms(&[RulepackDict::new(
71 "songs",
72 vec!["Song B".to_string()],
73 true,
74 )]);
75
76 let merged = DictionaryBundle::merge(a, b);
77 let entry = merged.get("songs").expect("entry");
78 assert_eq!(entry.terms(), &["Song B".to_string()]);
79 }
80
81 #[test]
82 fn extension_trait_restores_from_context_constructor() {
83 let ctx = Context {
84 dictionaries: HashMap::from([(
85 "dict_alpha".to_string(),
86 ContextDictionary {
87 terms: vec!["AAA-12345".to_string()],
88 case_sensitive: true,
89 },
90 )]),
91 class_map: HashMap::from([(
92 "dict_alpha".to_string(),
93 PiiClass::Custom("class_alpha".to_string()),
94 )]),
95 fields: Map::new(),
96 };
97
98 let bundle = DictionaryBundle::from_context(&ctx);
99 assert!(bundle.get("dict_alpha").is_some());
100 }
101}