1use std::ops::Range;
4use std::path::PathBuf;
5
6use roxmltree::Node;
7
8#[derive(Debug, thiserror::Error)]
10#[non_exhaustive]
11pub enum IncludeCause {
12 #[error("cyclic include: {}", cycle_display(chain))]
14 Cycle {
15 chain: Vec<PathBuf>,
17 },
18 #[error("cannot read {}: {source}", path.display())]
20 Io {
21 path: PathBuf,
23 #[source]
25 source: std::io::Error,
26 },
27 #[error("include file not found")]
29 NotFound,
30}
31
32fn cycle_display(chain: &[PathBuf]) -> String {
33 chain
34 .iter()
35 .map(|p| p.display().to_string())
36 .collect::<Vec<_>>()
37 .join(" -> ")
38}
39
40#[derive(Debug, thiserror::Error, miette::Diagnostic)]
43#[non_exhaustive]
44pub enum ParseError {
45 #[error("malformed XML: {message}")]
47 #[diagnostic(code(ergo_sbe::schema_parse::malformed_xml))]
48 MalformedXml {
49 message: String,
51 #[source_code]
53 source_code: miette::NamedSource<String>,
54 #[label("here")]
56 span: Option<miette::SourceSpan>,
57 },
58 #[error("missing {what}")]
60 #[diagnostic(code(ergo_sbe::schema_parse::missing))]
61 Missing {
62 what: String,
64 #[source_code]
66 source_code: miette::NamedSource<String>,
67 #[label("missing here")]
69 span: Option<miette::SourceSpan>,
70 },
71 #[error("invalid {what}: {value}")]
73 #[diagnostic(code(ergo_sbe::schema_parse::invalid))]
74 Invalid {
75 what: String,
77 value: String,
79 #[source_code]
81 source_code: miette::NamedSource<String>,
82 #[label("invalid here")]
84 span: Option<miette::SourceSpan>,
85 },
86 #[error("resolution error: {error}")]
88 #[diagnostic(code(ergo_sbe::schema_parse::resolve))]
89 Resolve {
90 #[source_code]
92 source_code: miette::NamedSource<String>,
93 #[label("here")]
95 span: Option<miette::SourceSpan>,
96 #[label("related")]
98 second_label: Option<miette::SourceSpan>,
99 #[source]
101 error: Box<crate::resolve::ResolveError>,
102 },
103 #[error("cannot read {}: {source}", path.display())]
105 #[diagnostic(code(ergo_sbe::schema_parse::io))]
106 Io {
107 path: PathBuf,
109 #[source]
111 source: std::io::Error,
112 },
113 #[error("include error for '{href}': {cause}")]
115 #[diagnostic(code(ergo_sbe::schema_parse::include))]
116 Include {
117 href: String,
119 attempted: Vec<PathBuf>,
121 #[source]
123 cause: IncludeCause,
124 #[source_code]
126 source_code: miette::NamedSource<String>,
127 #[label("include error here")]
129 span: Option<miette::SourceSpan>,
130 },
131}
132
133impl ParseError {
134 pub(crate) fn malformed_xml(name: &str, message: impl Into<String>, xml: &str) -> Self {
135 Self::MalformedXml {
136 message: message.into(),
137 source_code: named_source(name, xml),
138 span: None,
139 }
140 }
141
142 pub(crate) fn io(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
143 Self::Io {
144 path: path.into(),
145 source,
146 }
147 }
148
149 pub(crate) fn from_fault(name: &str, fault: Fault, input: &str) -> Self {
152 let source_code = named_source(name, input);
153 let span = fault.span.map(miette::SourceSpan::from);
154 match fault.kind {
155 FaultKind::Missing { what } => Self::Missing {
156 what,
157 source_code,
158 span,
159 },
160 FaultKind::Invalid { what, value } => Self::Invalid {
161 what,
162 value,
163 source_code,
164 span,
165 },
166 FaultKind::Include {
167 href,
168 attempted,
169 cause,
170 } => Self::Include {
171 href,
172 attempted,
173 cause,
174 source_code,
175 span,
176 },
177 }
178 }
179}
180
181impl From<crate::resolve::ResolveError> for ParseError {
182 fn from(mut e: crate::resolve::ResolveError) -> Self {
183 let source_code = e
184 .take_source_code()
185 .unwrap_or_else(|| miette::NamedSource::new("schema.xml", String::new()));
186 let (span, second_label) = e.take_spans();
187 Self::Resolve {
188 source_code,
189 span,
190 second_label,
191 error: Box::new(e),
192 }
193 }
194}
195
196pub(crate) fn named_source(name: &str, xml: &str) -> miette::NamedSource<String> {
200 miette::NamedSource::new(name, xml.to_owned())
201}
202
203#[derive(Debug)]
207pub(crate) struct Fault {
208 pub(crate) kind: FaultKind,
209 pub(crate) span: Option<Range<usize>>,
210}
211
212#[derive(Debug)]
213pub(crate) enum FaultKind {
214 Missing {
215 what: String,
216 },
217 Invalid {
218 what: String,
219 value: String,
220 },
221 Include {
222 href: String,
223 attempted: Vec<PathBuf>,
224 cause: IncludeCause,
225 },
226}
227
228impl Fault {
229 pub(crate) fn missing(node: Node<'_, '_>, what: impl Into<String>) -> Self {
230 Self {
231 kind: FaultKind::Missing { what: what.into() },
232 span: Some(node.range()),
233 }
234 }
235 pub(crate) fn missing_no_node(what: impl Into<String>) -> Self {
236 Self {
237 kind: FaultKind::Missing { what: what.into() },
238 span: None,
239 }
240 }
241
242 pub(crate) fn invalid(
243 node: Node<'_, '_>,
244 what: impl Into<String>,
245 value: impl Into<String>,
246 ) -> Self {
247 Self {
248 kind: FaultKind::Invalid {
249 what: what.into(),
250 value: value.into(),
251 },
252 span: Some(node.range()),
253 }
254 }
255
256 pub(crate) fn include(
257 href: impl Into<String>,
258 attempted: Vec<PathBuf>,
259 cause: IncludeCause,
260 ) -> Self {
261 Self {
262 kind: FaultKind::Include {
263 href: href.into(),
264 attempted,
265 cause,
266 },
267 span: None,
268 }
269 }
270}