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 TomlError {
30 pub const fn new(kind: TomlErrorKind, span: Span) -> Self {
32 TomlError {
33 kind,
34 span: Some(span),
35 source_code: None,
36 }
37 }
38
39 pub const fn without_span(kind: TomlErrorKind) -> Self {
41 TomlError {
42 kind,
43 span: None,
44 source_code: None,
45 }
46 }
47
48 pub fn with_source(mut self, source: &str) -> Self {
50 self.source_code = Some(source.to_string());
51 self
52 }
53}
54
55#[derive(Debug, Clone)]
57pub enum TomlErrorKind {
58 Parse(String),
60 UnexpectedType {
62 expected: &'static str,
64 got: &'static str,
66 },
67 UnexpectedEof {
69 expected: &'static str,
71 },
72 UnknownField {
74 field: String,
76 expected: Vec<&'static str>,
78 suggestion: Option<&'static str>,
80 },
81 MissingField {
83 field: &'static str,
85 table_start: Option<Span>,
87 table_end: Option<Span>,
89 },
90 InvalidValue {
92 message: String,
94 },
95 Reflect(ReflectError),
97 NumberOutOfRange {
99 value: String,
101 target_type: &'static str,
103 },
104 DuplicateKey {
106 key: String,
108 },
109 InvalidUtf8(core::str::Utf8Error),
111 Solver(String),
113 Serialize(String),
115}
116
117impl Display for TomlErrorKind {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 TomlErrorKind::Parse(msg) => write!(f, "parse error: {msg}"),
121 TomlErrorKind::UnexpectedType { expected, got } => {
122 write!(f, "type mismatch: expected {expected}, got {got}")
123 }
124 TomlErrorKind::UnexpectedEof { expected } => {
125 write!(f, "unexpected end of input, expected {expected}")
126 }
127 TomlErrorKind::UnknownField {
128 field,
129 expected,
130 suggestion,
131 } => {
132 write!(f, "unknown field `{field}`, expected one of: {expected:?}")?;
133 if let Some(suggested) = suggestion {
134 write!(f, " (did you mean `{suggested}`?)")?;
135 }
136 Ok(())
137 }
138 TomlErrorKind::MissingField { field, .. } => {
139 write!(f, "missing required field `{field}`")
140 }
141 TomlErrorKind::InvalidValue { message } => {
142 write!(f, "invalid value: {message}")
143 }
144 TomlErrorKind::Reflect(e) => write!(f, "reflection error: {e}"),
145 TomlErrorKind::NumberOutOfRange { value, target_type } => {
146 write!(f, "number `{value}` out of range for {target_type}")
147 }
148 TomlErrorKind::DuplicateKey { key } => {
149 write!(f, "duplicate key `{key}`")
150 }
151 TomlErrorKind::InvalidUtf8(e) => write!(f, "invalid UTF-8 sequence: {e}"),
152 TomlErrorKind::Solver(msg) => write!(f, "solver error: {msg}"),
153 TomlErrorKind::Serialize(msg) => write!(f, "serialization error: {msg}"),
154 }
155 }
156}
157
158impl TomlErrorKind {
159 pub const fn code(&self) -> &'static str {
161 match self {
162 TomlErrorKind::Parse(_) => "toml::parse",
163 TomlErrorKind::UnexpectedType { .. } => "toml::type_mismatch",
164 TomlErrorKind::UnexpectedEof { .. } => "toml::unexpected_eof",
165 TomlErrorKind::UnknownField { .. } => "toml::unknown_field",
166 TomlErrorKind::MissingField { .. } => "toml::missing_field",
167 TomlErrorKind::InvalidValue { .. } => "toml::invalid_value",
168 TomlErrorKind::Reflect(_) => "toml::reflect",
169 TomlErrorKind::NumberOutOfRange { .. } => "toml::number_out_of_range",
170 TomlErrorKind::DuplicateKey { .. } => "toml::duplicate_key",
171 TomlErrorKind::InvalidUtf8(_) => "toml::invalid_utf8",
172 TomlErrorKind::Solver(_) => "toml::solver",
173 TomlErrorKind::Serialize(_) => "toml::serialize",
174 }
175 }
176
177 pub fn label(&self) -> String {
179 match self {
180 TomlErrorKind::Parse(msg) => format!("parse error: {msg}"),
181 TomlErrorKind::UnexpectedType { expected, got } => {
182 format!("expected {expected}, got {got}")
183 }
184 TomlErrorKind::UnexpectedEof { expected } => format!("expected {expected}"),
185 TomlErrorKind::UnknownField {
186 field, suggestion, ..
187 } => {
188 if let Some(suggested) = suggestion {
189 format!("unknown field '{field}' - did you mean '{suggested}'?")
190 } else {
191 format!("unknown field '{field}'")
192 }
193 }
194 TomlErrorKind::MissingField { field, .. } => format!("missing field '{field}'"),
195 TomlErrorKind::InvalidValue { .. } => "invalid value".into(),
196 TomlErrorKind::Reflect(_) => "reflection error".into(),
197 TomlErrorKind::NumberOutOfRange { target_type, .. } => {
198 format!("out of range for {target_type}")
199 }
200 TomlErrorKind::DuplicateKey { key } => format!("duplicate key '{key}'"),
201 TomlErrorKind::InvalidUtf8(_) => "invalid UTF-8".into(),
202 TomlErrorKind::Solver(_) => "solver error".into(),
203 TomlErrorKind::Serialize(_) => "serialization error".into(),
204 }
205 }
206}
207
208impl From<ReflectError> for TomlError {
209 fn from(err: ReflectError) -> Self {
210 TomlError {
211 kind: TomlErrorKind::Reflect(err),
212 span: None,
213 source_code: None,
214 }
215 }
216}
217
218#[allow(dead_code)]
220pub type Result<T> = core::result::Result<T, TomlError>;