Skip to main content

jmdict_fast/
error.rs

1use std::fmt;
2
3/// Typed error enum for jmdict-fast, suitable for FFI consumers.
4#[derive(Debug)]
5pub enum JmdictError {
6    /// Data files not found at the expected path.
7    DataNotFound,
8    /// Binary format version mismatch between data and library.
9    DataVersionMismatch { expected: u32, found: u32 },
10    /// Data files are corrupted or have an invalid format.
11    DataCorrupted,
12    /// The query was invalid (e.g., empty string).
13    InvalidQuery,
14    /// An I/O error occurred while reading data files.
15    IoError(std::io::Error),
16    /// Failed to deserialize entry data.
17    DeserializationError,
18    /// `Dict::install*` requires an explicit cache directory on this
19    /// platform (typically iOS / Android / WASM, where the OS doesn't
20    /// expose a sensible writable default to native code). The host must
21    /// call `init_sdk_cache_dir(...)` or pass `InstallOptions::cache_dir(...)`.
22    #[cfg(feature = "install")]
23    CacheDirRequired { platform: &'static str },
24    /// A network request inside `Dict::install*` failed.
25    #[cfg(feature = "install")]
26    NetworkError(String),
27    /// [`crate::install::init_sdk_cache_dir`] was called more than once.
28    /// The cache root is process-global and first-set-wins, so a second
29    /// call is rejected rather than silently leaving previously-loaded
30    /// `Dict`s pointing at a different root than future installs.
31    #[cfg(feature = "install")]
32    CacheDirAlreadySet,
33}
34
35impl JmdictError {
36    /// Returns a stable numeric code for each error variant, useful for FFI.
37    pub fn code(&self) -> u32 {
38        match self {
39            JmdictError::DataNotFound => 1,
40            JmdictError::DataVersionMismatch { .. } => 2,
41            JmdictError::DataCorrupted => 3,
42            JmdictError::InvalidQuery => 4,
43            JmdictError::IoError(_) => 5,
44            JmdictError::DeserializationError => 6,
45            #[cfg(feature = "install")]
46            JmdictError::CacheDirRequired { .. } => 7,
47            #[cfg(feature = "install")]
48            JmdictError::NetworkError(_) => 8,
49            #[cfg(feature = "install")]
50            JmdictError::CacheDirAlreadySet => 9,
51        }
52    }
53}
54
55impl fmt::Display for JmdictError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            JmdictError::DataNotFound => {
59                write!(f, "Dictionary data files not found. Run `cargo xtask generate` or set JMDICT_DATA env var.")
60            }
61            JmdictError::DataVersionMismatch { expected, found } => {
62                write!(
63                    f,
64                    "Data format version {found}, library expects {expected}. Regenerate with cargo xtask generate."
65                )
66            }
67            JmdictError::DataCorrupted => {
68                write!(f, "Dictionary data is corrupted or has an invalid format.")
69            }
70            JmdictError::InvalidQuery => {
71                write!(f, "The search query is invalid.")
72            }
73            JmdictError::IoError(err) => {
74                write!(f, "I/O error: {err}")
75            }
76            JmdictError::DeserializationError => {
77                write!(f, "Failed to deserialize dictionary entry data.")
78            }
79            #[cfg(feature = "install")]
80            JmdictError::CacheDirRequired { platform } => {
81                write!(
82                    f,
83                    "Cache directory required on {platform}: this platform has no portable default. \
84                     Call `jmdict_fast::install::init_sdk_cache_dir(path)` from the host (e.g. via \
85                     path_provider on Flutter, Application.getCacheDir on Android, \
86                     FileManager URLs on iOS), or pass `InstallOptions::cache_dir(...)` per call."
87                )
88            }
89            #[cfg(feature = "install")]
90            JmdictError::NetworkError(msg) => write!(f, "Network error during install: {msg}"),
91            #[cfg(feature = "install")]
92            JmdictError::CacheDirAlreadySet => write!(
93                f,
94                "init_sdk_cache_dir was already called for this process; the cache root is one-shot."
95            ),
96        }
97    }
98}
99
100impl std::error::Error for JmdictError {
101    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
102        match self {
103            JmdictError::IoError(err) => Some(err),
104            _ => None,
105        }
106    }
107}
108
109impl From<std::io::Error> for JmdictError {
110    fn from(err: std::io::Error) -> Self {
111        // Map NotFound to DataNotFound for better semantics
112        if err.kind() == std::io::ErrorKind::NotFound {
113            JmdictError::DataNotFound
114        } else {
115            JmdictError::IoError(err)
116        }
117    }
118}
119
120impl From<fst::Error> for JmdictError {
121    fn from(_: fst::Error) -> Self {
122        JmdictError::DataCorrupted
123    }
124}