1use alloc::format;
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6use core::fmt::{self, Display};
7
8use facet_reflect::{ReflectError, Span};
9
10#[derive(Debug, Clone)]
12pub struct TomlError {
13 pub kind: TomlErrorKind,
15 pub span: Option<Span>,
17 pub source_code: Option<String>,
19}
20
21impl Display for TomlError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(f, "{}", self.kind)
24 }
25}
26
27impl std::error::Error for TomlError {}
28
29impl miette::Diagnostic for TomlError {
30 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
31 Some(Box::new(self.kind.code()))
32 }
33
34 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
35 self.source_code
36 .as_ref()
37 .map(|s| s as &dyn miette::SourceCode)
38 }
39
40 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
41 if let TomlErrorKind::MissingField {
43 field,
44 table_start,
45 table_end,
46 } = &self.kind
47 {
48 let mut labels = Vec::new();
49 if let Some(start) = table_start {
50 labels.push(miette::LabeledSpan::new(
51 Some("table started here".into()),
52 start.offset,
53 start.len,
54 ));
55 }
56 if let Some(end) = table_end {
57 labels.push(miette::LabeledSpan::new(
58 Some(format!("table ended without field `{field}`")),
59 end.offset,
60 end.len,
61 ));
62 }
63 if labels.is_empty() {
64 return None;
65 }
66 return Some(Box::new(labels.into_iter()));
67 }
68
69 let span = self.span?;
71 Some(Box::new(core::iter::once(miette::LabeledSpan::new(
72 Some(self.kind.label()),
73 span.offset,
74 span.len,
75 ))))
76 }
77}
78
79impl TomlError {
80 pub fn new(kind: TomlErrorKind, span: Span) -> Self {
82 TomlError {
83 kind,
84 span: Some(span),
85 source_code: None,
86 }
87 }
88
89 pub fn without_span(kind: TomlErrorKind) -> Self {
91 TomlError {
92 kind,
93 span: None,
94 source_code: None,
95 }
96 }
97
98 pub fn with_source(mut self, source: &str) -> Self {
100 self.source_code = Some(source.to_string());
101 self
102 }
103}
104
105#[derive(Debug, Clone)]
107pub enum TomlErrorKind {
108 Parse(String),
110 UnexpectedType {
112 expected: &'static str,
114 got: &'static str,
116 },
117 UnexpectedEof {
119 expected: &'static str,
121 },
122 UnknownField {
124 field: String,
126 expected: Vec<&'static str>,
128 suggestion: Option<&'static str>,
130 },
131 MissingField {
133 field: &'static str,
135 table_start: Option<Span>,
137 table_end: Option<Span>,
139 },
140 InvalidValue {
142 message: String,
144 },
145 Reflect(ReflectError),
147 NumberOutOfRange {
149 value: String,
151 target_type: &'static str,
153 },
154 DuplicateKey {
156 key: String,
158 },
159 InvalidUtf8,
161 Solver(String),
163 Serialize(String),
165}
166
167impl Display for TomlErrorKind {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 TomlErrorKind::Parse(msg) => write!(f, "parse error: {msg}"),
171 TomlErrorKind::UnexpectedType { expected, got } => {
172 write!(f, "type mismatch: expected {expected}, got {got}")
173 }
174 TomlErrorKind::UnexpectedEof { expected } => {
175 write!(f, "unexpected end of input, expected {expected}")
176 }
177 TomlErrorKind::UnknownField {
178 field,
179 expected,
180 suggestion,
181 } => {
182 write!(f, "unknown field `{field}`, expected one of: {expected:?}")?;
183 if let Some(suggested) = suggestion {
184 write!(f, " (did you mean `{suggested}`?)")?;
185 }
186 Ok(())
187 }
188 TomlErrorKind::MissingField { field, .. } => {
189 write!(f, "missing required field `{field}`")
190 }
191 TomlErrorKind::InvalidValue { message } => {
192 write!(f, "invalid value: {message}")
193 }
194 TomlErrorKind::Reflect(e) => write!(f, "reflection error: {e}"),
195 TomlErrorKind::NumberOutOfRange { value, target_type } => {
196 write!(f, "number `{value}` out of range for {target_type}")
197 }
198 TomlErrorKind::DuplicateKey { key } => {
199 write!(f, "duplicate key `{key}`")
200 }
201 TomlErrorKind::InvalidUtf8 => write!(f, "invalid UTF-8 sequence"),
202 TomlErrorKind::Solver(msg) => write!(f, "solver error: {msg}"),
203 TomlErrorKind::Serialize(msg) => write!(f, "serialization error: {msg}"),
204 }
205 }
206}
207
208impl TomlErrorKind {
209 pub fn code(&self) -> &'static str {
211 match self {
212 TomlErrorKind::Parse(_) => "toml::parse",
213 TomlErrorKind::UnexpectedType { .. } => "toml::type_mismatch",
214 TomlErrorKind::UnexpectedEof { .. } => "toml::unexpected_eof",
215 TomlErrorKind::UnknownField { .. } => "toml::unknown_field",
216 TomlErrorKind::MissingField { .. } => "toml::missing_field",
217 TomlErrorKind::InvalidValue { .. } => "toml::invalid_value",
218 TomlErrorKind::Reflect(_) => "toml::reflect",
219 TomlErrorKind::NumberOutOfRange { .. } => "toml::number_out_of_range",
220 TomlErrorKind::DuplicateKey { .. } => "toml::duplicate_key",
221 TomlErrorKind::InvalidUtf8 => "toml::invalid_utf8",
222 TomlErrorKind::Solver(_) => "toml::solver",
223 TomlErrorKind::Serialize(_) => "toml::serialize",
224 }
225 }
226
227 pub fn label(&self) -> String {
229 match self {
230 TomlErrorKind::Parse(msg) => format!("parse error: {msg}"),
231 TomlErrorKind::UnexpectedType { expected, got } => {
232 format!("expected {expected}, got {got}")
233 }
234 TomlErrorKind::UnexpectedEof { expected } => format!("expected {expected}"),
235 TomlErrorKind::UnknownField {
236 field, suggestion, ..
237 } => {
238 if let Some(suggested) = suggestion {
239 format!("unknown field '{field}' - did you mean '{suggested}'?")
240 } else {
241 format!("unknown field '{field}'")
242 }
243 }
244 TomlErrorKind::MissingField { field, .. } => format!("missing field '{field}'"),
245 TomlErrorKind::InvalidValue { .. } => "invalid value".into(),
246 TomlErrorKind::Reflect(_) => "reflection error".into(),
247 TomlErrorKind::NumberOutOfRange { target_type, .. } => {
248 format!("out of range for {target_type}")
249 }
250 TomlErrorKind::DuplicateKey { key } => format!("duplicate key '{key}'"),
251 TomlErrorKind::InvalidUtf8 => "invalid UTF-8".into(),
252 TomlErrorKind::Solver(_) => "solver error".into(),
253 TomlErrorKind::Serialize(_) => "serialization error".into(),
254 }
255 }
256}
257
258impl From<ReflectError> for TomlError {
259 fn from(err: ReflectError) -> Self {
260 TomlError {
261 kind: TomlErrorKind::Reflect(err),
262 span: None,
263 source_code: None,
264 }
265 }
266}
267
268#[allow(dead_code)]
270pub type Result<T> = core::result::Result<T, TomlError>;