es_fluent_derive_core/
error.rs1use proc_macro_error2::{abort, abort_call_site, emit_error};
4use proc_macro2::Span;
5use std::path::PathBuf;
6use thiserror::Error;
7use unic_langid::LanguageIdentifier;
8
9#[derive(Clone, Debug, thiserror::Error)]
11pub enum EsFluentCoreError {
12 #[error("Attribute error: {message}")]
14 AttributeError { message: String, span: Option<Span> },
15
16 #[error("Variant '{variant_name}' error: {message}")]
18 VariantError {
19 message: String,
20 variant_name: String,
21 span: Option<Span>,
22 },
23
24 #[error("{}", field_error_fmt(.message, .field_name))]
26 FieldError {
27 message: String,
28 field_name: Option<String>,
29 span: Option<Span>,
30 },
31
32 #[error("Transform error: {message}")]
34 TransformError { message: String, span: Option<Span> },
35}
36
37fn field_error_fmt(message: &str, field_name: &Option<String>) -> String {
38 match field_name {
39 Some(name) => format!("Field '{}' error: {}", name, message),
40 None => format!("Field error: {}", message),
41 }
42}
43
44impl EsFluentCoreError {
45 pub fn span(&self) -> Option<Span> {
47 match self {
48 EsFluentCoreError::AttributeError { span, .. } => *span,
49 EsFluentCoreError::VariantError { span, .. } => *span,
50 EsFluentCoreError::FieldError { span, .. } => *span,
51 EsFluentCoreError::TransformError { span, .. } => *span,
52 }
53 }
54
55 pub fn message_mut(&mut self) -> &mut String {
57 match self {
58 EsFluentCoreError::AttributeError { message, .. } => message,
59 EsFluentCoreError::VariantError { message, .. } => message,
60 EsFluentCoreError::FieldError { message, .. } => message,
61 EsFluentCoreError::TransformError { message, .. } => message,
62 }
63 }
64
65 pub fn abort(self) -> ! {
67 let msg = self.to_string();
68 match self.span() {
69 Some(span) => abort!(span, "{}", msg),
70 None => abort_call_site!("{}", msg),
71 }
72 }
73
74 pub fn emit(&self) {
76 let msg = self.to_string();
77 match self.span() {
78 Some(span) => emit_error!(span, "{}", msg),
79 None => emit_error!("{}", msg),
80 }
81 }
82}
83
84pub trait ErrorExt {
86 fn with_note(self, note: String) -> Self;
88 fn with_help(self, help: String) -> Self;
90}
91
92impl ErrorExt for EsFluentCoreError {
93 fn with_note(mut self, note_msg: String) -> Self {
94 let message = self.message_mut();
95 *message = format!("{}\nnote: {}", message, note_msg);
96 self
97 }
98
99 fn with_help(mut self, help_msg: String) -> Self {
100 let message = self.message_mut();
101 *message = format!("{}\nhelp: {}", message, help_msg);
102 self
103 }
104}
105
106pub type EsFluentCoreResult<T> = Result<T, EsFluentCoreError>;
108
109#[derive(Debug, Error)]
111pub enum EsFluentError {
112 #[error("Configuration file not found: {path}")]
114 ConfigNotFound { path: PathBuf },
115
116 #[error("Failed to parse configuration file: {0}")]
118 ConfigParseError(#[from] toml::de::Error),
119
120 #[error("Assets directory not found: {path}")]
122 AssetsNotFound { path: PathBuf },
123
124 #[error("Fallback language directory not found: {language}")]
126 FallbackLanguageNotFound { language: String },
127
128 #[error("Invalid language identifier '{identifier}': {reason}")]
130 InvalidLanguageIdentifier { identifier: String, reason: String },
131
132 #[error("Language '{0}' is not supported")]
134 LanguageNotSupported(LanguageIdentifier),
135
136 #[error("Fluent parsing error: {0:?}")]
138 FluentParseError(Vec<fluent_syntax::parser::ParserError>),
139
140 #[error("Fluent serialization error: {0}")]
142 FluentSerializeError(#[from] std::fmt::Error),
143
144 #[error("IO error: {0}")]
146 IoError(#[from] std::io::Error),
147
148 #[error("Environment variable error: {0}")]
150 EnvVarError(#[from] std::env::VarError),
151
152 #[error("Backend error: {0}")]
154 BackendError(#[from] anyhow::Error),
155
156 #[error("JSON error: {0}")]
158 JsonError(#[from] serde_json::Error),
159
160 #[error("Missing package name")]
162 MissingPackageName,
163}
164
165impl EsFluentError {
166 pub fn config_not_found(path: impl Into<PathBuf>) -> Self {
168 Self::ConfigNotFound { path: path.into() }
169 }
170
171 pub fn assets_not_found(path: impl Into<PathBuf>) -> Self {
173 Self::AssetsNotFound { path: path.into() }
174 }
175
176 pub fn invalid_language_identifier(
178 identifier: impl Into<String>,
179 reason: impl Into<String>,
180 ) -> Self {
181 Self::InvalidLanguageIdentifier {
182 identifier: identifier.into(),
183 reason: reason.into(),
184 }
185 }
186
187 pub fn fallback_language_not_found(language: impl Into<String>) -> Self {
189 Self::FallbackLanguageNotFound {
190 language: language.into(),
191 }
192 }
193}
194
195pub type EsFluentResult<T> = Result<T, EsFluentError>;