1//! # Generic builder type implementations
2//!
3//! This module contains [`FromEventsBuilder`] implementations for types from
4//! foreign libraries (such as the standard library).
5//!
6//! In order to not clutter the `xso` crate's main namespace, they are
7//! stashed away in a separate module.
8
9// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
10//
11// This Source Code Form is subject to the terms of the Mozilla Public
12// License, v. 2.0. If a copy of the MPL was not distributed with this
13// file, You can obtain one at http://mozilla.org/MPL/2.0/.
14
15use alloc::boxed::Box;
16
17use crate::error::{Error, FromEventsError};
18use crate::{FromEventsBuilder, FromXml};
19
20/// Helper struct to construct an `Option<T>` from XML events.
21pub struct OptionBuilder<T: FromEventsBuilder>(T);
22
23impl<T: FromEventsBuilder> FromEventsBuilder for OptionBuilder<T> {
24 type Output = Option<T::Output>;
25
26 fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
27 self.0.feed(ev).map(|ok| ok.map(|value| Some(value)))
28 }
29}
30
31/// Parsers `T` into `Some(.)`.
32///
33/// Note that this never generates `None`: The main use case is to allow
34/// external (i.e. without calling `from_events`) defaulting to `None` and
35/// for optional serialisation (the [`AsXml`][`crate::AsXml`] implementation
36/// on `Option<T>` emits nothing for `None`).
37impl<T: FromXml> FromXml for Option<T> {
38 type Builder = OptionBuilder<T::Builder>;
39
40 fn from_events(
41 name: rxml::QName,
42 attrs: rxml::AttrMap,
43 ) -> Result<Self::Builder, FromEventsError> {
44 Ok(OptionBuilder(T::from_events(name, attrs)?))
45 }
46}
47
48/// Helper struct to construct an `Box<T>` from XML events.
49pub struct BoxBuilder<T: FromEventsBuilder>(Box<T>);
50
51impl<T: FromEventsBuilder> FromEventsBuilder for BoxBuilder<T> {
52 type Output = Box<T::Output>;
53
54 fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
55 self.0.feed(ev).map(|ok| ok.map(|value| Box::new(value)))
56 }
57}
58
59/// Parsers `T` into a `Box`.
60impl<T: FromXml> FromXml for Box<T> {
61 type Builder = BoxBuilder<T::Builder>;
62
63 fn from_events(
64 name: rxml::QName,
65 attrs: rxml::AttrMap,
66 ) -> Result<Self::Builder, FromEventsError> {
67 Ok(BoxBuilder(Box::new(T::from_events(name, attrs)?)))
68 }
69}
70
71#[derive(Debug)]
72enum FallibleBuilderInner<T: FromEventsBuilder, E> {
73 Processing { depth: usize, builder: T },
74 Failed { depth: usize, err: Option<E> },
75 Done,
76}
77
78/// Build a `Result<T, E>` from XML.
79///
80/// This builder, invoked generally via the [`FromXml`] implementation on
81/// `Result<T, E> where T: FromXml, E: From<Error>`, allows to fallably parse
82/// an XSO from XML.
83///
84/// If an error occurs while parsing the XSO, the remaining events which
85/// belong to that XSO are discarded. Once all events have been seen, the
86/// error is returned as `Err(.)` value.
87///
88/// If parsing succeeds, the parsed XSO is returned as `Ok(.)` value.
89#[derive(Debug)]
90pub struct FallibleBuilder<T: FromEventsBuilder, E>(FallibleBuilderInner<T, E>);
91
92impl<T: FromEventsBuilder, E: From<Error>> FromEventsBuilder for FallibleBuilder<T, E> {
93 type Output = Result<T::Output, E>;
94
95 fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
96 match self.0 {
97 FallibleBuilderInner::Processing {
98 ref mut depth,
99 ref mut builder,
100 } => {
101 let new_depth = match ev {
102 rxml::Event::StartElement(..) => match depth.checked_add(1) {
103 // I *think* it is OK to return an err here
104 // instead of panicking. The reason is that anyone
105 // who intends to resume processing at the level
106 // of where we started to parse this thing in case
107 // of an error either has to:
108 // - Use this fallible implementation and rely on
109 // it capturing the error (which we don't in
110 // this case).
111 // - Or count the depth themselves, which will
112 // either fail in the same way, or they use a
113 // wider type (in which case it's ok).
114 None => {
115 self.0 = FallibleBuilderInner::Done;
116 return Err(Error::Other("maximum XML nesting depth exceeded"));
117 }
118 Some(v) => Some(v),
119 },
120 // In case of an element end, underflow means that we
121 // have reached the end of the XSO we wanted to process.
122 // We handle that case at the end of the outer match's
123 // body: Either we have returned a value then (good), or,
124 // if we reach the end there with a new_depth == None,
125 // something went horribly wrong (and we panic).
126 rxml::Event::EndElement(..) => depth.checked_sub(1),
127
128 // Text and XML declarations have no influence on parsing
129 // depth.
130 rxml::Event::XmlDeclaration(..) | rxml::Event::Text(..) => Some(*depth),
131 };
132
133 match builder.feed(ev) {
134 Ok(Some(v)) => {
135 self.0 = FallibleBuilderInner::Done;
136 return Ok(Some(Ok(v)));
137 }
138 Ok(None) => {
139 // continue processing in the next round.
140 }
141 Err(e) => {
142 // We are now officially failed ..
143 match new_depth {
144 // .. but we are not done yet, so enter the
145 // failure backtracking state.
146 Some(depth) => {
147 self.0 = FallibleBuilderInner::Failed {
148 depth,
149 err: Some(e.into()),
150 };
151 return Ok(None);
152 }
153 // .. and we are done with parsing, so we return
154 // the error as value.
155 None => {
156 self.0 = FallibleBuilderInner::Done;
157 return Ok(Some(Err(e.into())));
158 }
159 }
160 }
161 };
162
163 *depth = match new_depth {
164 Some(v) => v,
165 None => unreachable!("fallible parsing continued beyond end of element"),
166 };
167
168 // Need more events.
169 Ok(None)
170 }
171 FallibleBuilderInner::Failed {
172 ref mut depth,
173 ref mut err,
174 } => {
175 *depth = match ev {
176 rxml::Event::StartElement(..) => match depth.checked_add(1) {
177 // See above for error return rationale.
178 None => {
179 self.0 = FallibleBuilderInner::Done;
180 return Err(Error::Other("maximum XML nesting depth exceeded"));
181 }
182 Some(v) => v,
183 },
184 rxml::Event::EndElement(..) => match depth.checked_sub(1) {
185 Some(v) => v,
186 None => {
187 // We are officially done, return a value, switch
188 // states, and be done with it.
189 let err = err.take().expect("fallible parsing somehow lost its error");
190 self.0 = FallibleBuilderInner::Done;
191 return Ok(Some(Err(err)));
192 }
193 },
194
195 // Text and XML declarations have no influence on parsing
196 // depth.
197 rxml::Event::XmlDeclaration(..) | rxml::Event::Text(..) => *depth,
198 };
199
200 // Need more events
201 Ok(None)
202 }
203 FallibleBuilderInner::Done => {
204 panic!("FromEventsBuilder called after it returned a value")
205 }
206 }
207 }
208}
209
210/// Parsers `T` fallibly. See [`FallibleBuilder`] for details.
211impl<T: FromXml, E: From<Error>> FromXml for Result<T, E> {
212 type Builder = FallibleBuilder<T::Builder, E>;
213
214 fn from_events(
215 name: rxml::QName,
216 attrs: rxml::AttrMap,
217 ) -> Result<Self::Builder, FromEventsError> {
218 match T::from_events(name, attrs) {
219 Ok(builder) => Ok(FallibleBuilder(FallibleBuilderInner::Processing {
220 depth: 0,
221 builder,
222 })),
223 Err(FromEventsError::Mismatch { name, attrs }) => {
224 Err(FromEventsError::Mismatch { name, attrs })
225 }
226 Err(FromEventsError::Invalid(e)) => Ok(FallibleBuilder(FallibleBuilderInner::Failed {
227 depth: 0,
228 err: Some(e.into()),
229 })),
230 }
231 }
232}
233
234/// Builder which discards an entire child tree without inspecting the
235/// contents.
236#[derive(Debug)]
237pub struct Discard {
238 depth: usize,
239}
240
241impl Discard {
242 /// Create a new discarding builder.
243 pub fn new() -> Self {
244 Self { depth: 0 }
245 }
246}
247
248impl FromEventsBuilder for Discard {
249 type Output = ();
250
251 fn feed(&mut self, ev: rxml::Event) -> Result<Option<Self::Output>, Error> {
252 match ev {
253 rxml::Event::StartElement(..) => {
254 self.depth = match self.depth.checked_add(1) {
255 Some(v) => v,
256 None => return Err(Error::Other("maximum XML nesting depth exceeded")),
257 };
258 Ok(None)
259 }
260 rxml::Event::EndElement(..) => match self.depth.checked_sub(1) {
261 None => Ok(Some(())),
262 Some(v) => {
263 self.depth = v;
264 Ok(None)
265 }
266 },
267 _ => Ok(None),
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 use alloc::borrow::ToOwned;
277 use rxml::{parser::EventMetrics, Event, Namespace, NcName};
278
279 macro_rules! null_builder {
280 ($name:ident for $output:ident) => {
281 #[derive(Debug)]
282 enum $name {}
283
284 impl FromEventsBuilder for $name {
285 type Output = $output;
286
287 fn feed(&mut self, _: Event) -> Result<Option<Self::Output>, Error> {
288 unreachable!();
289 }
290 }
291 };
292 }
293
294 null_builder!(AlwaysMismatchBuilder for AlwaysMismatch);
295 null_builder!(InitialErrorBuilder for InitialError);
296
297 #[derive(Debug)]
298 struct AlwaysMismatch;
299
300 impl FromXml for AlwaysMismatch {
301 type Builder = AlwaysMismatchBuilder;
302
303 fn from_events(
304 name: rxml::QName,
305 attrs: rxml::AttrMap,
306 ) -> Result<Self::Builder, FromEventsError> {
307 Err(FromEventsError::Mismatch { name, attrs })
308 }
309 }
310
311 #[derive(Debug)]
312 struct InitialError;
313
314 impl FromXml for InitialError {
315 type Builder = InitialErrorBuilder;
316
317 fn from_events(_: rxml::QName, _: rxml::AttrMap) -> Result<Self::Builder, FromEventsError> {
318 Err(FromEventsError::Invalid(Error::Other("some error")))
319 }
320 }
321
322 #[derive(Debug)]
323 struct FailOnContentBuilder;
324
325 impl FromEventsBuilder for FailOnContentBuilder {
326 type Output = FailOnContent;
327
328 fn feed(&mut self, _: Event) -> Result<Option<Self::Output>, Error> {
329 Err(Error::Other("content error"))
330 }
331 }
332
333 #[derive(Debug)]
334 struct FailOnContent;
335
336 impl FromXml for FailOnContent {
337 type Builder = FailOnContentBuilder;
338
339 fn from_events(_: rxml::QName, _: rxml::AttrMap) -> Result<Self::Builder, FromEventsError> {
340 Ok(FailOnContentBuilder)
341 }
342 }
343
344 fn qname() -> rxml::QName {
345 (Namespace::NONE, NcName::try_from("test").unwrap())
346 }
347
348 fn attrs() -> rxml::AttrMap {
349 rxml::AttrMap::new()
350 }
351
352 #[test]
353 fn fallible_builder_mismatch_passthrough() {
354 match Result::<AlwaysMismatch, Error>::from_events(qname(), attrs()) {
355 Err(FromEventsError::Mismatch { .. }) => (),
356 other => panic!("unexpected result: {:?}", other),
357 }
358 }
359
360 #[test]
361 fn fallible_builder_initial_error_capture() {
362 let mut builder = match Result::<InitialError, Error>::from_events(qname(), attrs()) {
363 Ok(v) => v,
364 other => panic!("unexpected result: {:?}", other),
365 };
366 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
367 Ok(None) => (),
368 other => panic!("unexpected result: {:?}", other),
369 };
370 match builder.feed(Event::EndElement(EventMetrics::zero())) {
371 Ok(Some(Err(Error::Other("some error")))) => (),
372 other => panic!("unexpected result: {:?}", other),
373 };
374 }
375
376 #[test]
377 fn fallible_builder_initial_error_capture_allows_nested_stuff() {
378 let mut builder = match Result::<InitialError, Error>::from_events(qname(), attrs()) {
379 Ok(v) => v,
380 other => panic!("unexpected result: {:?}", other),
381 };
382 match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
383 Ok(None) => (),
384 other => panic!("unexpected result: {:?}", other),
385 };
386 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
387 Ok(None) => (),
388 other => panic!("unexpected result: {:?}", other),
389 };
390 match builder.feed(Event::EndElement(EventMetrics::zero())) {
391 Ok(None) => (),
392 other => panic!("unexpected result: {:?}", other),
393 };
394 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
395 Ok(None) => (),
396 other => panic!("unexpected result: {:?}", other),
397 };
398 match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
399 Ok(None) => (),
400 other => panic!("unexpected result: {:?}", other),
401 };
402 match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
403 Ok(None) => (),
404 other => panic!("unexpected result: {:?}", other),
405 };
406 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
407 Ok(None) => (),
408 other => panic!("unexpected result: {:?}", other),
409 };
410 match builder.feed(Event::EndElement(EventMetrics::zero())) {
411 Ok(None) => (),
412 other => panic!("unexpected result: {:?}", other),
413 };
414 match builder.feed(Event::EndElement(EventMetrics::zero())) {
415 Ok(None) => (),
416 other => panic!("unexpected result: {:?}", other),
417 };
418 match builder.feed(Event::EndElement(EventMetrics::zero())) {
419 Ok(Some(Err(Error::Other("some error")))) => (),
420 other => panic!("unexpected result: {:?}", other),
421 };
422 }
423
424 #[test]
425 fn fallible_builder_content_error_capture() {
426 let mut builder = match Result::<FailOnContent, Error>::from_events(qname(), attrs()) {
427 Ok(v) => v,
428 other => panic!("unexpected result: {:?}", other),
429 };
430 match builder.feed(Event::EndElement(EventMetrics::zero())) {
431 Ok(Some(Err(Error::Other("content error")))) => (),
432 other => panic!("unexpected result: {:?}", other),
433 };
434 }
435
436 #[test]
437 fn fallible_builder_content_error_capture_with_more_content() {
438 let mut builder = match Result::<FailOnContent, Error>::from_events(qname(), attrs()) {
439 Ok(v) => v,
440 other => panic!("unexpected result: {:?}", other),
441 };
442 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
443 Ok(None) => (),
444 other => panic!("unexpected result: {:?}", other),
445 };
446 match builder.feed(Event::EndElement(EventMetrics::zero())) {
447 Ok(Some(Err(Error::Other("content error")))) => (),
448 other => panic!("unexpected result: {:?}", other),
449 };
450 }
451
452 #[test]
453 fn fallible_builder_content_error_capture_with_nested_content() {
454 let mut builder = match Result::<FailOnContent, Error>::from_events(qname(), attrs()) {
455 Ok(v) => v,
456 other => panic!("unexpected result: {:?}", other),
457 };
458 match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
459 Ok(None) => (),
460 other => panic!("unexpected result: {:?}", other),
461 };
462 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
463 Ok(None) => (),
464 other => panic!("unexpected result: {:?}", other),
465 };
466 match builder.feed(Event::EndElement(EventMetrics::zero())) {
467 Ok(None) => (),
468 other => panic!("unexpected result: {:?}", other),
469 };
470 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
471 Ok(None) => (),
472 other => panic!("unexpected result: {:?}", other),
473 };
474 match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
475 Ok(None) => (),
476 other => panic!("unexpected result: {:?}", other),
477 };
478 match builder.feed(Event::StartElement(EventMetrics::zero(), qname(), attrs())) {
479 Ok(None) => (),
480 other => panic!("unexpected result: {:?}", other),
481 };
482 match builder.feed(Event::Text(EventMetrics::zero(), "hello world!".to_owned())) {
483 Ok(None) => (),
484 other => panic!("unexpected result: {:?}", other),
485 };
486 match builder.feed(Event::EndElement(EventMetrics::zero())) {
487 Ok(None) => (),
488 other => panic!("unexpected result: {:?}", other),
489 };
490 match builder.feed(Event::EndElement(EventMetrics::zero())) {
491 Ok(None) => (),
492 other => panic!("unexpected result: {:?}", other),
493 };
494 match builder.feed(Event::EndElement(EventMetrics::zero())) {
495 Ok(Some(Err(Error::Other("content error")))) => (),
496 other => panic!("unexpected result: {:?}", other),
497 };
498 }
499}