1extern crate alloc;
4
5use alloc::boxed::Box;
6use alloc::format;
7use alloc::string::{String, ToString};
8use alloc::vec::Vec;
9use core::fmt::{self, Display};
10
11use facet_reflect::ReflectError;
12use miette::NamedSource;
13
14pub use facet_reflect::Span;
16
17pub(crate) trait SpanExt {
19 fn from_saphyr_span(span: &saphyr_parser::Span) -> Span {
21 let start = span.start.index();
22 let end = span.end.index();
23 Span::new(start, end.saturating_sub(start))
24 }
25}
26
27impl SpanExt for Span {}
28
29#[derive(Debug)]
31pub struct YamlError {
32 pub kind: YamlErrorKind,
34 pub span: Option<Span>,
36 pub source_code: Option<Box<NamedSource<String>>>,
39}
40
41impl Display for YamlError {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 write!(f, "{}", self.kind)
44 }
45}
46
47impl core::error::Error for YamlError {}
48
49impl miette::Diagnostic for YamlError {
50 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
51 Some(Box::new(self.kind.code()))
52 }
53
54 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
55 self.source_code
56 .as_ref()
57 .map(|s| s.as_ref() as &dyn miette::SourceCode)
58 }
59
60 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
61 let span = self.span?;
62 Some(Box::new(core::iter::once(miette::LabeledSpan::new(
63 Some(self.kind.label()),
64 span.offset,
65 span.len.max(1), ))))
67 }
68}
69
70impl YamlError {
71 pub fn new(kind: YamlErrorKind, span: Span) -> Self {
73 YamlError {
74 kind,
75 span: Some(span),
76 source_code: None,
77 }
78 }
79
80 pub fn without_span(kind: YamlErrorKind) -> Self {
82 YamlError {
83 kind,
84 span: None,
85 source_code: None,
86 }
87 }
88
89 pub fn with_source(mut self, source: &str) -> Self {
91 self.source_code = Some(Box::new(NamedSource::new("input.yaml", source.to_string())));
92 self
93 }
94}
95
96#[derive(Debug)]
98pub enum YamlErrorKind {
99 Parse(String),
101 UnexpectedEvent {
103 got: String,
105 expected: &'static str,
107 },
108 UnexpectedEof {
110 expected: &'static str,
112 },
113 TypeMismatch {
115 expected: &'static str,
117 got: &'static str,
119 },
120 UnknownField {
122 field: String,
124 expected: Vec<&'static str>,
126 suggestion: Option<&'static str>,
128 },
129 MissingField {
131 field: &'static str,
133 },
134 InvalidValue {
136 message: String,
138 },
139 Reflect(ReflectError),
141 NumberOutOfRange {
143 value: String,
145 target_type: &'static str,
147 },
148 DuplicateKey {
150 key: String,
152 },
153 InvalidUtf8(core::str::Utf8Error),
155 Solver(String),
157 Unsupported(String),
159 Io(String),
161}
162
163impl Display for YamlErrorKind {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 match self {
166 YamlErrorKind::Parse(e) => write!(f, "YAML parse error: {e}"),
167 YamlErrorKind::UnexpectedEvent { got, expected } => {
168 write!(f, "unexpected YAML event: got {got}, expected {expected}")
169 }
170 YamlErrorKind::UnexpectedEof { expected } => {
171 write!(f, "unexpected end of input, expected {expected}")
172 }
173 YamlErrorKind::TypeMismatch { expected, got } => {
174 write!(f, "type mismatch: expected {expected}, got {got}")
175 }
176 YamlErrorKind::UnknownField {
177 field, expected, ..
178 } => {
179 write!(f, "unknown field `{field}`, expected one of: {expected:?}")
180 }
181 YamlErrorKind::MissingField { field } => {
182 write!(f, "missing required field `{field}`")
183 }
184 YamlErrorKind::InvalidValue { message } => {
185 write!(f, "invalid value: {message}")
186 }
187 YamlErrorKind::Reflect(e) => write!(f, "reflection error: {e}"),
188 YamlErrorKind::NumberOutOfRange { value, target_type } => {
189 write!(f, "number `{value}` out of range for {target_type}")
190 }
191 YamlErrorKind::DuplicateKey { key } => {
192 write!(f, "duplicate key `{key}`")
193 }
194 YamlErrorKind::InvalidUtf8(e) => write!(f, "invalid UTF-8 sequence: {e}"),
195 YamlErrorKind::Solver(msg) => write!(f, "solver error: {msg}"),
196 YamlErrorKind::Unsupported(msg) => write!(f, "unsupported: {msg}"),
197 YamlErrorKind::Io(msg) => write!(f, "IO error: {msg}"),
198 }
199 }
200}
201
202impl YamlErrorKind {
203 pub fn code(&self) -> &'static str {
205 match self {
206 YamlErrorKind::Parse(_) => "yaml::parse",
207 YamlErrorKind::UnexpectedEvent { .. } => "yaml::unexpected_event",
208 YamlErrorKind::UnexpectedEof { .. } => "yaml::unexpected_eof",
209 YamlErrorKind::TypeMismatch { .. } => "yaml::type_mismatch",
210 YamlErrorKind::UnknownField { .. } => "yaml::unknown_field",
211 YamlErrorKind::MissingField { .. } => "yaml::missing_field",
212 YamlErrorKind::InvalidValue { .. } => "yaml::invalid_value",
213 YamlErrorKind::Reflect(_) => "yaml::reflect",
214 YamlErrorKind::NumberOutOfRange { .. } => "yaml::number_out_of_range",
215 YamlErrorKind::DuplicateKey { .. } => "yaml::duplicate_key",
216 YamlErrorKind::InvalidUtf8(_) => "yaml::invalid_utf8",
217 YamlErrorKind::Solver(_) => "yaml::solver",
218 YamlErrorKind::Unsupported(_) => "yaml::unsupported",
219 YamlErrorKind::Io(_) => "yaml::io",
220 }
221 }
222
223 pub fn label(&self) -> String {
225 match self {
226 YamlErrorKind::Parse(_) => "parse error here".to_string(),
227 YamlErrorKind::UnexpectedEvent { got, .. } => format!("unexpected {got}"),
228 YamlErrorKind::UnexpectedEof { expected } => format!("expected {expected}"),
229 YamlErrorKind::TypeMismatch { expected, got } => {
230 format!("expected {expected}, got {got}")
231 }
232 YamlErrorKind::UnknownField { field, .. } => format!("unknown field `{field}`"),
233 YamlErrorKind::MissingField { field } => format!("missing `{field}`"),
234 YamlErrorKind::InvalidValue { message } => message.clone(),
235 YamlErrorKind::Reflect(e) => format!("{e}"),
236 YamlErrorKind::NumberOutOfRange { target_type, .. } => {
237 format!("out of range for {target_type}")
238 }
239 YamlErrorKind::DuplicateKey { key } => format!("duplicate key `{key}`"),
240 YamlErrorKind::InvalidUtf8(_) => "invalid UTF-8".to_string(),
241 YamlErrorKind::Solver(msg) => msg.clone(),
242 YamlErrorKind::Unsupported(msg) => msg.clone(),
243 YamlErrorKind::Io(msg) => msg.clone(),
244 }
245 }
246}
247
248impl From<ReflectError> for YamlError {
249 fn from(e: ReflectError) -> Self {
250 YamlError::without_span(YamlErrorKind::Reflect(e))
251 }
252}
253
254impl From<ReflectError> for YamlErrorKind {
255 fn from(e: ReflectError) -> Self {
256 YamlErrorKind::Reflect(e)
257 }
258}