es_fluent_shared/
error.rs1use std::path::PathBuf;
4use thiserror::Error;
5use unic_langid::LanguageIdentifier;
6
7#[derive(Debug, Error)]
9pub enum EsFluentError {
10 #[error("Configuration file not found: {path}")]
12 ConfigNotFound { path: PathBuf },
13
14 #[error("Failed to parse configuration file: {0}")]
16 ConfigParseError(#[from] toml::de::Error),
17
18 #[error("Assets directory not found: {path}")]
20 AssetsNotFound { path: PathBuf },
21
22 #[error("Fallback language directory not found: {language}")]
24 FallbackLanguageNotFound { language: String },
25
26 #[error("Invalid language identifier '{identifier}': {reason}")]
28 InvalidLanguageIdentifier { identifier: String, reason: String },
29
30 #[error("Invalid Fluent metadata '{identifier}': {reason}")]
32 InvalidFluentIdentifier { identifier: String, reason: String },
33
34 #[error("Duplicate generated FTL key '{key}' from {first} and {second}")]
36 DuplicateGeneratedFtlKey {
37 key: String,
38 first: String,
39 second: String,
40 },
41
42 #[error("Language '{0}' is not supported")]
44 LanguageNotSupported(LanguageIdentifier),
45
46 #[error("Fluent parsing error: {0:?}")]
48 FluentParseError(Vec<fluent_syntax::parser::ParserError>),
49
50 #[error("Fluent serialization error: {0}")]
52 FluentSerializeError(#[from] std::fmt::Error),
53
54 #[error("IO error: {0}")]
56 IoError(#[from] std::io::Error),
57
58 #[error("Environment variable error: {0}")]
60 EnvVarError(#[from] std::env::VarError),
61
62 #[error("Backend error: {0}")]
64 BackendError(#[from] anyhow::Error),
65
66 #[error("Missing package name")]
68 MissingPackageName,
69}
70
71impl EsFluentError {
72 pub fn config_not_found(path: impl Into<PathBuf>) -> Self {
74 Self::ConfigNotFound { path: path.into() }
75 }
76
77 pub fn assets_not_found(path: impl Into<PathBuf>) -> Self {
79 Self::AssetsNotFound { path: path.into() }
80 }
81
82 pub fn invalid_language_identifier(
84 identifier: impl Into<String>,
85 reason: impl Into<String>,
86 ) -> Self {
87 Self::InvalidLanguageIdentifier {
88 identifier: identifier.into(),
89 reason: reason.into(),
90 }
91 }
92
93 pub fn invalid_fluent_identifier(
95 identifier: impl Into<String>,
96 reason: impl Into<String>,
97 ) -> Self {
98 Self::InvalidFluentIdentifier {
99 identifier: identifier.into(),
100 reason: reason.into(),
101 }
102 }
103
104 pub fn duplicate_generated_ftl_key(
106 key: impl Into<String>,
107 first: impl Into<String>,
108 second: impl Into<String>,
109 ) -> Self {
110 Self::DuplicateGeneratedFtlKey {
111 key: key.into(),
112 first: first.into(),
113 second: second.into(),
114 }
115 }
116
117 pub fn fallback_language_not_found(language: impl Into<String>) -> Self {
119 Self::FallbackLanguageNotFound {
120 language: language.into(),
121 }
122 }
123}
124
125pub type EsFluentResult<T> = Result<T, EsFluentError>;
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use std::error::Error as _;
132
133 #[test]
134 fn helper_constructors_build_expected_variants() {
135 let config = EsFluentError::config_not_found("/tmp/i18n.toml");
136 assert!(matches!(config, EsFluentError::ConfigNotFound { .. }));
137
138 let assets = EsFluentError::assets_not_found("/tmp/i18n");
139 assert!(matches!(assets, EsFluentError::AssetsNotFound { .. }));
140
141 let invalid = EsFluentError::invalid_language_identifier("bad", "parse failure");
142 assert!(matches!(
143 invalid,
144 EsFluentError::InvalidLanguageIdentifier { .. }
145 ));
146
147 let invalid_fluent = EsFluentError::invalid_fluent_identifier("bad key", "parse failure");
148 assert!(matches!(
149 invalid_fluent,
150 EsFluentError::InvalidFluentIdentifier { .. }
151 ));
152
153 let fallback = EsFluentError::fallback_language_not_found("en-US");
154 assert!(matches!(
155 fallback,
156 EsFluentError::FallbackLanguageNotFound { .. }
157 ));
158 }
159
160 #[test]
161 fn display_messages_include_variant_context() {
162 let supported = EsFluentError::LanguageNotSupported("fr".parse().unwrap());
163 assert_eq!(supported.to_string(), "Language 'fr' is not supported");
164
165 let fluent = EsFluentError::FluentParseError(Vec::new());
166 assert_eq!(fluent.to_string(), "Fluent parsing error: []");
167
168 let serialize = EsFluentError::FluentSerializeError(std::fmt::Error);
169 assert_eq!(
170 serialize.to_string(),
171 "Fluent serialization error: an error occurred when formatting an argument"
172 );
173
174 let missing = EsFluentError::MissingPackageName;
175 assert_eq!(missing.to_string(), "Missing package name");
176 }
177
178 #[test]
179 fn source_errors_are_preserved_for_wrapped_variants() {
180 let parse_error: EsFluentError = toml::from_str::<toml::Value>("=").unwrap_err().into();
181 assert!(parse_error.source().is_some());
182
183 let io_error: EsFluentError =
184 std::io::Error::new(std::io::ErrorKind::NotFound, "missing").into();
185 assert!(io_error.source().is_some());
186
187 let env_error: EsFluentError = std::env::VarError::NotPresent.into();
188 assert!(env_error.source().is_some());
189
190 let backend_error: EsFluentError = anyhow::anyhow!("backend").into();
191 assert!(backend_error.source().is_some());
192 }
193}