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("Language '{0}' is not supported")]
32 LanguageNotSupported(LanguageIdentifier),
33
34 #[error("Fluent parsing error: {0:?}")]
36 FluentParseError(Vec<fluent_syntax::parser::ParserError>),
37
38 #[error("Fluent serialization error: {0}")]
40 FluentSerializeError(#[from] std::fmt::Error),
41
42 #[error("IO error: {0}")]
44 IoError(#[from] std::io::Error),
45
46 #[error("Environment variable error: {0}")]
48 EnvVarError(#[from] std::env::VarError),
49
50 #[error("Backend error: {0}")]
52 BackendError(#[from] anyhow::Error),
53
54 #[error("Missing package name")]
56 MissingPackageName,
57}
58
59impl EsFluentError {
60 pub fn config_not_found(path: impl Into<PathBuf>) -> Self {
62 Self::ConfigNotFound { path: path.into() }
63 }
64
65 pub fn assets_not_found(path: impl Into<PathBuf>) -> Self {
67 Self::AssetsNotFound { path: path.into() }
68 }
69
70 pub fn invalid_language_identifier(
72 identifier: impl Into<String>,
73 reason: impl Into<String>,
74 ) -> Self {
75 Self::InvalidLanguageIdentifier {
76 identifier: identifier.into(),
77 reason: reason.into(),
78 }
79 }
80
81 pub fn fallback_language_not_found(language: impl Into<String>) -> Self {
83 Self::FallbackLanguageNotFound {
84 language: language.into(),
85 }
86 }
87}
88
89pub type EsFluentResult<T> = Result<T, EsFluentError>;
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use std::error::Error;
96
97 #[test]
98 fn helper_constructors_build_expected_variants() {
99 let config = EsFluentError::config_not_found("/tmp/i18n.toml");
100 assert!(matches!(config, EsFluentError::ConfigNotFound { .. }));
101
102 let assets = EsFluentError::assets_not_found("/tmp/i18n");
103 assert!(matches!(assets, EsFluentError::AssetsNotFound { .. }));
104
105 let invalid = EsFluentError::invalid_language_identifier("bad", "parse failure");
106 assert!(matches!(
107 invalid,
108 EsFluentError::InvalidLanguageIdentifier { .. }
109 ));
110
111 let fallback = EsFluentError::fallback_language_not_found("en-US");
112 assert!(matches!(
113 fallback,
114 EsFluentError::FallbackLanguageNotFound { .. }
115 ));
116 }
117
118 #[test]
119 fn display_messages_include_variant_context() {
120 let supported = EsFluentError::LanguageNotSupported("fr".parse().unwrap());
121 assert_eq!(supported.to_string(), "Language 'fr' is not supported");
122
123 let fluent = EsFluentError::FluentParseError(Vec::new());
124 assert_eq!(fluent.to_string(), "Fluent parsing error: []");
125
126 let serialize = EsFluentError::FluentSerializeError(std::fmt::Error);
127 assert_eq!(
128 serialize.to_string(),
129 "Fluent serialization error: an error occurred when formatting an argument"
130 );
131
132 let missing = EsFluentError::MissingPackageName;
133 assert_eq!(missing.to_string(), "Missing package name");
134 }
135
136 #[test]
137 fn source_errors_are_preserved_for_wrapped_variants() {
138 let parse_error: EsFluentError = toml::from_str::<toml::Value>("=").unwrap_err().into();
139 assert!(parse_error.source().is_some());
140
141 let io_error: EsFluentError =
142 std::io::Error::new(std::io::ErrorKind::NotFound, "missing").into();
143 assert!(io_error.source().is_some());
144
145 let env_error: EsFluentError = std::env::VarError::NotPresent.into();
146 assert!(env_error.source().is_some());
147
148 let backend_error: EsFluentError = anyhow::anyhow!("backend").into();
149 assert!(backend_error.source().is_some());
150 }
151}