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}