Skip to main content

fluent_typed/
error.rs

1//! The runtime error type, [`L10nError`].
2
3use std::fmt;
4
5/// An error from loading a localization bundle or formatting one of its
6/// messages at runtime.
7///
8/// Returned by [`L10nBundle`](crate::prelude::L10nBundle),
9/// [`L10nLanguageVec`](crate::prelude::L10nLanguageVec) and the generated
10/// `L10nLanguage::new`. The generated message accessors `unwrap` this: a failure
11/// there means the generated code and the embedded `.ftl` have drifted apart,
12/// which is a build-time bug rather than something to handle at runtime.
13///
14/// The variants carry owned strings rather than `fluent-bundle`'s own error
15/// types on purpose, so this enum does not change shape when that (pre-1.0)
16/// dependency is updated. It is `#[non_exhaustive]` so new variants can be added
17/// in a minor release.
18#[derive(Debug)]
19#[non_exhaustive]
20pub enum L10nError {
21    /// The user-supplied decompressor (for a compressed single-file build)
22    /// returned an error. Carries that error's message.
23    Decompression(String),
24
25    /// The `.ftl` bytes were not valid UTF-8.
26    InvalidUtf8(std::string::FromUtf8Error),
27
28    /// The language identifier (e.g. `"en-US"`) could not be parsed.
29    InvalidLanguage {
30        /// The identifier that failed to parse.
31        lang: String,
32        /// A human-readable description of why it failed.
33        reason: String,
34    },
35
36    /// The Fluent builtins (`NUMBER`, …) could not be registered on the bundle.
37    Builtins(String),
38
39    /// The `.ftl` resource failed to parse. One entry per parser error.
40    ResourceParse(Vec<String>),
41
42    /// The resource could not be added to the bundle (e.g. a message id that
43    /// collides with one already present).
44    AddResource(Vec<String>),
45
46    /// No message with this id exists in the bundle.
47    MessageNotFound {
48        /// The message id that was looked up.
49        id: String,
50    },
51
52    /// The message exists but has no value of its own (it has only attributes).
53    MessageHasNoValue {
54        /// The message id that was looked up.
55        id: String,
56    },
57
58    /// The message exists but has no attribute with this name.
59    AttributeNotFound {
60        /// The message id.
61        id: String,
62        /// The attribute that was looked up.
63        attribute: String,
64    },
65
66    /// Formatting the message or attribute failed — typically a missing or
67    /// wrongly-typed argument. One entry per formatting error.
68    Format {
69        /// The message id being formatted.
70        id: String,
71        /// The attribute being formatted, if any.
72        attribute: Option<String>,
73        /// One entry per formatting error reported by fluent-bundle.
74        errors: Vec<String>,
75    },
76}
77
78impl fmt::Display for L10nError {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        match self {
81            Self::Decompression(e) => write!(f, "Could not decompress the .ftl data: {e}"),
82            Self::InvalidUtf8(e) => write!(f, "The .ftl content is not valid UTF-8: {e}"),
83            Self::InvalidLanguage { lang, reason } => {
84                write!(f, "Invalid language identifier '{lang}': {reason}")
85            }
86            Self::Builtins(e) => write!(f, "Could not register the Fluent builtins: {e}"),
87            Self::ResourceParse(errors) => {
88                write!(f, "Could not parse the .ftl resource:")?;
89                for e in errors {
90                    write!(f, "\n  {e}")?;
91                }
92                Ok(())
93            }
94            Self::AddResource(errors) => {
95                write!(f, "Could not add the .ftl resource to the bundle:")?;
96                for e in errors {
97                    write!(f, "\n  {e}")?;
98                }
99                Ok(())
100            }
101            Self::MessageNotFound { id } => write!(f, "No message '{id}' found"),
102            Self::MessageHasNoValue { id } => {
103                write!(f, "Message '{id}' has no value of its own")
104            }
105            Self::AttributeNotFound { id, attribute } => {
106                write!(f, "Message '{id}' has no attribute '{attribute}'")
107            }
108            Self::Format {
109                id,
110                attribute,
111                errors,
112            } => {
113                match attribute {
114                    Some(a) => write!(f, "Could not format attribute '{a}' of message '{id}':")?,
115                    None => write!(f, "Could not format message '{id}':")?,
116                }
117                for e in errors {
118                    write!(f, "\n  {e}")?;
119                }
120                Ok(())
121            }
122        }
123    }
124}
125
126impl std::error::Error for L10nError {
127    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
128        match self {
129            Self::InvalidUtf8(e) => Some(e),
130            _ => None,
131        }
132    }
133}