Skip to content

Commit 702d374

Browse files
committed
refactor(rust): improve error reporting by adopting exn inspired extension
1 parent 118deb8 commit 702d374

File tree

13 files changed

+496
-401
lines changed

13 files changed

+496
-401
lines changed

src/dictionary/loader.rs

Lines changed: 74 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use log::{error, info};
1212
#[cfg(feature = "sqlite")]
1313
use super::SqliteDictionary;
1414
use super::{Dictionary, TrieBuf, uhash};
15+
use crate::exn::{Exn, ResultExt};
1516
use crate::{
1617
dictionary::DictionaryUsage,
1718
editor::{AbbrevTable, SymbolSelector},
@@ -33,27 +34,6 @@ pub struct AssetLoader {
3334
search_path: Option<String>,
3435
}
3536

36-
/// Errors during loading system or user dictionaries.
37-
#[derive(Debug)]
38-
pub enum LoadDictionaryError {
39-
/// Cannot find any system or user dictionary.
40-
NotFound,
41-
/// IO Error.
42-
IoError(io::Error),
43-
}
44-
45-
impl Display for LoadDictionaryError {
46-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47-
write!(f, "Unable to load system dictionary: {self:?}")
48-
}
49-
}
50-
51-
impl Error for LoadDictionaryError {}
52-
53-
fn io_err(err: io::Error) -> LoadDictionaryError {
54-
LoadDictionaryError::IoError(err)
55-
}
56-
5737
impl AssetLoader {
5838
/// Creates a new dictionary loader.
5939
pub fn new() -> AssetLoader {
@@ -112,29 +92,33 @@ impl AssetLoader {
11292
}
11393
/// Loads the abbrev table.
11494
pub fn load_abbrev(&self) -> Result<AbbrevTable, LoadDictionaryError> {
95+
let error = || LoadDictionaryError::new("failed to load abbrev table");
96+
let not_found = || error().with_source(io::Error::from(io::ErrorKind::NotFound));
11597
let search_path = if let Some(path) = &self.search_path {
11698
path.to_owned()
11799
} else {
118100
search_path_from_env_var()
119101
};
120-
let parent_path = find_path_by_files(&search_path, &[ABBREV_FILE_NAME])
121-
.ok_or(LoadDictionaryError::NotFound)?;
102+
let parent_path =
103+
find_path_by_files(&search_path, &[ABBREV_FILE_NAME]).or_raise(not_found)?;
122104
let abbrev_path = parent_path.join(ABBREV_FILE_NAME);
123105
info!("Loading {ABBREV_FILE_NAME}");
124-
AbbrevTable::open(abbrev_path).map_err(io_err)
106+
AbbrevTable::open(abbrev_path).or_raise(error)
125107
}
126108
/// Loads the symbol table.
127109
pub fn load_symbol_selector(&self) -> Result<SymbolSelector, LoadDictionaryError> {
110+
let error = || LoadDictionaryError::new("failed to load symbol table");
111+
let not_found = || error().with_source(io::Error::from(io::ErrorKind::NotFound));
128112
let search_path = if let Some(path) = &self.search_path {
129113
path.to_owned()
130114
} else {
131115
search_path_from_env_var()
132116
};
133-
let parent_path = find_path_by_files(&search_path, &[SYMBOLS_FILE_NAME])
134-
.ok_or(LoadDictionaryError::NotFound)?;
117+
let parent_path =
118+
find_path_by_files(&search_path, &[SYMBOLS_FILE_NAME]).or_raise(not_found)?;
135119
let symbol_path = parent_path.join(SYMBOLS_FILE_NAME);
136120
info!("Loading {SYMBOLS_FILE_NAME}");
137-
SymbolSelector::open(symbol_path).map_err(io_err)
121+
SymbolSelector::open(symbol_path).or_raise(error)
138122
}
139123
}
140124

@@ -171,34 +155,39 @@ impl UserDictionaryManager {
171155
///
172156
/// If no user dictionary were found, a new dictionary will be created at
173157
/// the default path.
174-
pub fn init(&self) -> io::Result<Box<dyn Dictionary>> {
158+
pub fn init(&self) -> Result<Box<dyn Dictionary>, LoadDictionaryError> {
159+
let error = || LoadDictionaryError::new("failed to init user dictionary");
160+
let not_found = || error().with_source(io::Error::from(io::ErrorKind::NotFound));
175161
let mut loader = SingleDictionaryLoader::new();
176162
loader.migrate_sqlite(true);
177163
let data_path = self
178164
.data_path
179165
.clone()
180166
.or_else(userphrase_path)
181-
.ok_or(io::Error::from(io::ErrorKind::NotFound))?;
167+
.or_raise(not_found)?;
182168
if data_path.ends_with(UD_MEM_FILE_NAME) {
183169
return Ok(Self::in_memory());
184170
}
185171
if data_path.exists() {
186172
info!("Use existing user dictionary {}", data_path.display());
187-
return loader.guess_format_and_load(&data_path).map(|mut dict| {
188-
dict.set_usage(DictionaryUsage::User);
189-
dict
190-
});
173+
return loader
174+
.guess_format_and_load(&data_path)
175+
.map(|mut dict| {
176+
dict.set_usage(DictionaryUsage::User);
177+
dict
178+
})
179+
.or_raise(error);
191180
}
192181
let userdata_dir = data_path.parent().expect("path should contain a filename");
193182
if !userdata_dir.exists() {
194183
info!("Creating userdata_dir: {}", userdata_dir.display());
195-
fs::create_dir_all(userdata_dir)?;
184+
fs::create_dir_all(userdata_dir).or_raise(error)?;
196185
}
197186
info!(
198187
"Creating a fresh user dictionary at {}",
199188
data_path.display()
200189
);
201-
let mut fresh_dict = loader.guess_format_and_load(&data_path)?;
190+
let mut fresh_dict = loader.guess_format_and_load(&data_path).or_raise(error)?;
202191

203192
let user_dict_path = userdata_dir.join(UD_SQLITE_FILE_NAME);
204193
if cfg!(feature = "sqlite") && user_dict_path.exists() {
@@ -208,18 +197,15 @@ impl UserDictionaryManager {
208197
"Importing existing sqlite dictionary at {}",
209198
user_dict_path.display()
210199
);
211-
let dict = SqliteDictionary::open(user_dict_path)
212-
.map_err(|e| io::Error::new(io::ErrorKind::Other, Box::new(e)))?;
200+
let dict = SqliteDictionary::open(user_dict_path).or_raise(error)?;
213201
for (syllables, phrase) in dict.entries() {
214202
let freq = phrase.freq();
215203
let last_used = phrase.last_used().unwrap_or_default();
216204
fresh_dict
217205
.update_phrase(&syllables, phrase, freq, last_used)
218-
.map_err(|e| io::Error::new(io::ErrorKind::Other, Box::new(e)))?;
206+
.or_raise(error)?;
219207
}
220-
fresh_dict
221-
.flush()
222-
.map_err(|e| io::Error::new(io::ErrorKind::Other, Box::new(e)))?;
208+
fresh_dict.flush().or_raise(error)?;
223209
}
224210
} else {
225211
let uhash_path = userdata_dir.join(UD_UHASH_FILE_NAME);
@@ -228,7 +214,7 @@ impl UserDictionaryManager {
228214
"Importing existing uhash dictionary at {}",
229215
user_dict_path.display()
230216
);
231-
let mut input = File::open(uhash_path)?;
217+
let mut input = File::open(uhash_path).or_raise(error)?;
232218
if let Ok(phrases) = uhash::try_load_bin(&input).or_else(|_| {
233219
input.rewind()?;
234220
uhash::try_load_text(&input)
@@ -238,11 +224,9 @@ impl UserDictionaryManager {
238224
let last_used = phrase.last_used().unwrap_or_default();
239225
fresh_dict
240226
.update_phrase(&syllables, phrase, freq, last_used)
241-
.map_err(|e| io::Error::other(Box::new(e)))?;
227+
.or_raise(error)?;
242228
}
243-
fresh_dict
244-
.flush()
245-
.map_err(|e| io::Error::other(Box::new(e)))?;
229+
fresh_dict.flush().or_raise(error)?;
246230
}
247231
}
248232
}
@@ -254,20 +238,24 @@ impl UserDictionaryManager {
254238
///
255239
/// If no user exclusion dictionary were found, a new dictionary
256240
/// will be created at the default path.
257-
pub fn init_deleted(&self) -> io::Result<Box<dyn Dictionary>> {
241+
pub fn init_deleted(&self) -> Result<Box<dyn Dictionary>, LoadDictionaryError> {
242+
let error = || LoadDictionaryError::new("failed to init user exclusion dictionary");
243+
let not_found = || error().with_source(io::Error::from(io::ErrorKind::NotFound));
258244
let loader = SingleDictionaryLoader::new();
259245
let data_path = self
260246
.data_path
261247
.clone()
262248
.or_else(userphrase_path)
263-
.ok_or(io::Error::from(io::ErrorKind::NotFound))?;
249+
.or_raise(not_found)?;
264250
let userdata_dir = data_path.parent().expect("path should contain a filename");
265251
if !userdata_dir.exists() {
266252
info!("Creating userdata_dir: {}", userdata_dir.display());
267-
fs::create_dir_all(&userdata_dir)?;
253+
fs::create_dir_all(&userdata_dir).or_raise(error)?;
268254
}
269255
let exclude_dict_path = userdata_dir.join("chewing-deleted.dat");
270-
Ok(loader.guess_format_and_load(&exclude_dict_path)?)
256+
loader
257+
.guess_format_and_load(&exclude_dict_path)
258+
.or_raise(error)
271259
}
272260
/// Load a in-memory user dictionary.
273261
pub fn in_memory() -> Box<dyn Dictionary> {
@@ -290,12 +278,16 @@ impl SingleDictionaryLoader {
290278
pub fn migrate_sqlite(&mut self, migrate: bool) {
291279
self.migrate_sqlite = migrate;
292280
}
293-
pub fn guess_format_and_load(&self, dict_path: &PathBuf) -> io::Result<Box<dyn Dictionary>> {
281+
pub fn guess_format_and_load(
282+
&self,
283+
dict_path: &PathBuf,
284+
) -> Result<Box<dyn Dictionary>, LoadDictionaryError> {
285+
let error = || LoadDictionaryError::new("failed to parse and load dictionary");
294286
info!("Loading dictionary {}", dict_path.display());
295287
if self.migrate_sqlite && dict_path.is_file() {
296-
let metadata = dict_path.metadata()?;
288+
let metadata = dict_path.metadata().or_raise(error)?;
297289
if metadata.permissions().readonly() {
298-
return Err(io::Error::from(io::ErrorKind::PermissionDenied));
290+
return Err(error().with_source(io::Error::from(io::ErrorKind::PermissionDenied)));
299291
}
300292
}
301293

@@ -306,23 +298,47 @@ impl SingleDictionaryLoader {
306298
if self.migrate_sqlite {
307299
SqliteDictionary::open(dict_path)
308300
.map(|dict| Box::new(dict) as Box<dyn Dictionary>)
309-
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, Box::new(e)))
301+
.or_raise(error)
310302
} else {
311303
SqliteDictionary::open_readonly(dict_path)
312304
.map(|dict| Box::new(dict) as Box<dyn Dictionary>)
313-
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, Box::new(e)))
305+
.or_raise(error)
314306
}
315307
}
316308
#[cfg(not(feature = "sqlite"))]
317309
{
318-
Err(io::Error::from(io::ErrorKind::Unsupported))
310+
Err(error().with_source(io::Error::from(io::ErrorKind::Unsupported)))
319311
}
320312
} else if ext.eq_ignore_ascii_case("dat") {
321313
TrieBuf::open(dict_path)
322314
.map(|dict| Box::new(dict) as Box<dyn Dictionary>)
323-
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, Box::new(e)))
315+
.or_raise(error)
324316
} else {
325-
Err(io::Error::from(io::ErrorKind::Other))
317+
Err(error())
326318
}
327319
}
328320
}
321+
322+
/// Errors during loading system or user dictionaries.
323+
#[derive(Debug)]
324+
pub struct LoadDictionaryError {
325+
msg: String,
326+
source: Option<Box<dyn Error + Send + Sync + 'static>>,
327+
}
328+
329+
impl LoadDictionaryError {
330+
fn new(msg: &str) -> LoadDictionaryError {
331+
LoadDictionaryError {
332+
msg: msg.to_string(),
333+
source: None,
334+
}
335+
}
336+
}
337+
338+
impl Display for LoadDictionaryError {
339+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340+
f.write_str(&self.msg)
341+
}
342+
}
343+
344+
impl_exn!(LoadDictionaryError);

0 commit comments

Comments
 (0)