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 /// External `.ftl` content failed validation against the message contract
78 /// compiled into the generated API (see [`validate_ftl`](crate::validate_ftl)
79 /// and the generated `L10nLanguage::new_external`). Every violation is
80 /// listed, not just the first.
81 Validation {
82 /// One entry per contract violation.
83 violations: Vec<crate::ContractViolation>,
84 },
85}
86
87impl fmt::Display for L10nError {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 match self {
90 Self::Decompression(e) => write!(f, "Could not decompress the .ftl data: {e}"),
91 Self::InvalidUtf8(e) => write!(f, "The .ftl content is not valid UTF-8: {e}"),
92 Self::InvalidLanguage { lang, reason } => {
93 write!(f, "Invalid language identifier '{lang}': {reason}")
94 }
95 Self::Builtins(e) => write!(f, "Could not register the Fluent builtins: {e}"),
96 Self::ResourceParse(errors) => {
97 write!(f, "Could not parse the .ftl resource:")?;
98 for e in errors {
99 write!(f, "\n {e}")?;
100 }
101 Ok(())
102 }
103 Self::AddResource(errors) => {
104 write!(f, "Could not add the .ftl resource to the bundle:")?;
105 for e in errors {
106 write!(f, "\n {e}")?;
107 }
108 Ok(())
109 }
110 Self::MessageNotFound { id } => write!(f, "No message '{id}' found"),
111 Self::MessageHasNoValue { id } => {
112 write!(f, "Message '{id}' has no value of its own")
113 }
114 Self::AttributeNotFound { id, attribute } => {
115 write!(f, "Message '{id}' has no attribute '{attribute}'")
116 }
117 Self::Format {
118 id,
119 attribute,
120 errors,
121 } => {
122 match attribute {
123 Some(a) => write!(f, "Could not format attribute '{a}' of message '{id}':")?,
124 None => write!(f, "Could not format message '{id}':")?,
125 }
126 for e in errors {
127 write!(f, "\n {e}")?;
128 }
129 Ok(())
130 }
131 Self::Validation { violations } => {
132 write!(
133 f,
134 "The .ftl content does not satisfy the compiled message contract:"
135 )?;
136 for v in violations {
137 write!(f, "\n {v}")?;
138 }
139 Ok(())
140 }
141 }
142 }
143}
144
145impl std::error::Error for L10nError {
146 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
147 match self {
148 Self::InvalidUtf8(e) => Some(e),
149 _ => None,
150 }
151 }
152}