sudachi/dic/
dictionary.rs1use std::fs::File;
18use std::path::Path;
19
20use memmap2::Mmap;
21
22use crate::analysis::morpheme::SingleMorpheme;
23use crate::config::Config;
24use crate::dic::binary_loader::BinaryDictionary;
25use crate::dic::character_category::CharacterCategory;
26use crate::dic::description::Description;
27use crate::dic::error::DictionaryCompatibilityError;
28use crate::dic::grammar::Grammar;
29use crate::dic::lexicon::Lexicon;
30use crate::dic::lexicon_set::LexiconSet;
31use crate::dic::storage::{Storage, SudachiDicData};
32use crate::dic::subset::InfoSubset;
33use crate::dic::{
34 lookup_all_entries, DescriptionAccess, DictionaryAccess, LexiconAccess, ReferenceIdAccess,
35};
36use crate::error::SudachiError;
37use crate::error::SudachiResult;
38use crate::plugin::input_text::InputTextPlugin;
39use crate::plugin::oov::OovProviderPlugin;
40use crate::plugin::path_rewrite::PathRewritePlugin;
41use crate::plugin::Plugins;
42
43pub struct JapaneseDictionary {
53 storage: SudachiDicData,
54 plugins: Plugins,
55 description: Description,
56 _grammar: Grammar<'static>,
58 _lexicon: LexiconSet<'static>,
60}
61
62fn map_file(path: &Path) -> SudachiResult<Storage> {
63 let file = File::open(path)?;
64 let mapping = unsafe { Mmap::map(&file) }?;
65 Ok(Storage::File(mapping))
66}
67
68fn load_system_dic(cfg: &Config) -> SudachiResult<Storage> {
69 let p = cfg.resolved_system_dict()?;
70 map_file(&p).map_err(|e| e.with_context(p.as_os_str().to_string_lossy()))
71}
72
73impl JapaneseDictionary {
74 pub fn from_cfg(cfg: &Config) -> SudachiResult<JapaneseDictionary> {
77 let mut sb = SudachiDicData::new(load_system_dic(cfg)?);
78
79 for udic in cfg.resolved_user_dicts()? {
80 sb.add_user(
81 map_file(&udic).map_err(|e| e.with_context(udic.as_os_str().to_string_lossy()))?,
82 )
83 }
84
85 let chardef = CharacterCategory::from_bytes(
86 &cfg.resolve_resource(&cfg.character_definition_file)?
87 .read_bytes()?,
88 )?;
89
90 Self::from_cfg_storage_chardef(cfg, sb, chardef)
91 }
92
93 pub fn from_cfg_storage(
95 cfg: &Config,
96 storage: SudachiDicData,
97 ) -> SudachiResult<JapaneseDictionary> {
98 let chardef = CharacterCategory::from_bytes(
99 &cfg.resolve_resource(&cfg.character_definition_file)?
100 .read_bytes()?,
101 )?;
102 Self::from_cfg_storage_chardef(cfg, storage, chardef)
103 }
104
105 #[deprecated(
106 since = "0.7.0",
107 note = "embedded resources are now resolved through Config; use from_cfg_storage instead"
108 )]
109 pub fn from_cfg_storage_with_embedded_chardef(
111 cfg: &Config,
112 storage: SudachiDicData,
113 ) -> SudachiResult<JapaneseDictionary> {
114 let chardef = CharacterCategory::from_embedded();
115 Self::from_cfg_storage_chardef(cfg, storage, chardef)
116 }
117
118 pub fn from_cfg_storage_chardef(
119 cfg: &Config,
120 storage: SudachiDicData,
121 chardef: CharacterCategory,
122 ) -> SudachiResult<JapaneseDictionary> {
123 let system_binary =
124 BinaryDictionary::load_system(unsafe { storage.system_static_slice() })?;
125 let system_signature = system_binary.compatibility_key().to_owned();
126 let description = system_binary.description.clone();
127
128 let mut grammar = Grammar::from_system_binary(system_binary.grammar)?;
129 grammar.set_character_category(chardef);
130
131 let lexicon_set =
132 LexiconSet::from_system_binary(system_binary.lexicon, grammar.pos_list.len());
133
134 let plugins = { Plugins::load(cfg, &mut grammar)? };
135 if plugins.oov.is_empty() {
136 return Err(SudachiError::NoOOVPluginProvided);
137 }
138 for p in plugins.connect_cost.plugins() {
139 p.edit(&mut grammar);
140 }
141
142 let mut dic = JapaneseDictionary {
143 storage,
144 plugins,
145 description,
146 _grammar: grammar,
147 _lexicon: lexicon_set,
148 };
149
150 let user_dicts: Vec<_> = dic.storage.user_static_slice();
152 for (user_index, udic) in user_dicts.into_iter().enumerate() {
153 let user_dict = BinaryDictionary::load_user(udic)?;
154 if user_dict.compatibility_key() != system_signature {
155 return Err(DictionaryCompatibilityError::UserDictionary {
156 user_index,
157 system_signature: system_signature.clone(),
158 user_reference: user_dict.compatibility_key().to_owned(),
159 }
160 .into());
161 }
162 dic = dic.merge_user_dictionary(user_dict)?;
163 }
164
165 Ok(dic)
166 }
167
168 pub fn grammar(&self) -> &Grammar<'_> {
170 &self._grammar
171 }
172
173 pub fn lexicon(&self) -> &LexiconSet<'_> {
175 &self._lexicon
176 }
177
178 pub fn description(&self) -> &Description {
179 &self.description
180 }
181
182 pub fn entries(&self) -> impl Iterator<Item = SudachiResult<SingleMorpheme<&Self>>> + '_ {
190 self.entries_subset(InfoSubset::all())
191 }
192
193 pub fn entries_subset(
195 &self,
196 subset: InfoSubset,
197 ) -> impl Iterator<Item = SudachiResult<SingleMorpheme<&Self>>> + '_ {
198 self.lexicon()
199 .word_ids()
200 .map(move |word_id| SingleMorpheme::from_word_id(self, word_id?, subset))
201 }
202
203 pub fn lookup_all_entries(&self, surface: &str) -> SudachiResult<Vec<SingleMorpheme<&Self>>> {
209 self.lookup_all_entries_subset(surface, InfoSubset::all())
210 }
211
212 pub fn lookup_all_entries_subset(
214 &self,
215 surface: &str,
216 subset: InfoSubset,
217 ) -> SudachiResult<Vec<SingleMorpheme<&Self>>> {
218 lookup_all_entries(self, surface, subset)
219 }
220
221 pub fn oov_morpheme(&self, pos_id: u16, surface: &str) -> SudachiResult<SingleMorpheme<&Self>> {
225 self.oov_morpheme_with_forms(pos_id, surface, surface, surface, surface)
226 }
227
228 pub fn oov_morpheme_with_forms(
230 &self,
231 pos_id: u16,
232 surface: &str,
233 reading: &str,
234 normalized_form: &str,
235 dictionary_form: &str,
236 ) -> SudachiResult<SingleMorpheme<&Self>> {
237 SingleMorpheme::oov(
238 self,
239 pos_id,
240 surface.to_owned(),
241 reading.to_owned(),
242 normalized_form.to_owned(),
243 dictionary_form.to_owned(),
244 )
245 }
246
247 fn merge_user_dictionary(
248 mut self,
249 user_dict: BinaryDictionary<'static>,
250 ) -> SudachiResult<Self> {
251 let mut user_lexicon = Lexicon::from_binary(user_dict.lexicon);
253 user_lexicon.update_cost(&self)?;
254 self._lexicon
255 .append(user_lexicon, self._grammar.pos_list.len())?;
256
257 self._grammar.merge_binary(user_dict.grammar);
258
259 Ok(self)
260 }
261}
262
263impl LexiconAccess for JapaneseDictionary {
264 fn lexicon(&self) -> &LexiconSet<'_> {
265 self.lexicon()
266 }
267}
268
269impl DictionaryAccess for JapaneseDictionary {
270 fn grammar(&self) -> &Grammar<'_> {
271 self.grammar()
272 }
273
274 fn input_text_plugins(&self) -> &[Box<dyn InputTextPlugin + Sync + Send>] {
275 self.plugins.input_text.plugins()
276 }
277
278 fn oov_provider_plugins(&self) -> &[Box<dyn OovProviderPlugin + Sync + Send>] {
279 self.plugins.oov.plugins()
280 }
281
282 fn path_rewrite_plugins(&self) -> &[Box<dyn PathRewritePlugin + Sync + Send>] {
283 self.plugins.path_rewrite.plugins()
284 }
285}
286
287impl DescriptionAccess for JapaneseDictionary {
288 fn description(&self) -> &Description {
289 &self.description
290 }
291}
292
293impl ReferenceIdAccess for JapaneseDictionary {
294 fn reference_ids(&self) -> std::collections::HashMap<u32, String> {
295 BinaryDictionary::load_system(unsafe { self.storage.system_static_slice() })
296 .and_then(|dict| dict.reference_id_table())
297 .unwrap_or_default()
298 }
299}