From 9ec9a0f0c6e9b93dd711c46aa96d83e79f00453c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Fri, 21 Jun 2024 18:02:55 +0200 Subject: [PATCH] xso: introduce xso::from_bytes This is useful both for doctests and other scenarios where you just want to parse a bit of XML you already have. --- xso/src/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/xso/src/lib.rs b/xso/src/lib.rs index f18ae7f..8b68df9 100644 --- a/xso/src/lib.rs +++ b/xso/src/lib.rs @@ -173,3 +173,58 @@ pub fn try_from_element( // implementations must be constructible from that. unreachable!("minidom::Element did not produce enough events to complete element") } + +fn map_nonio_error(r: Result) -> Result { + match r { + Ok(v) => Ok(v), + Err(rxml::Error::IO(_)) => unreachable!(), + Err(rxml::Error::Xml(e)) => Err(e.into()), + Err(rxml::Error::InvalidUtf8Byte(_)) => Err(self::error::Error::Other("invalid utf-8")), + Err(rxml::Error::InvalidChar(_)) => { + Err(self::error::Error::Other("non-character encountered")) + } + Err(rxml::Error::RestrictedXml(_)) => Err(self::error::Error::Other("restricted xml")), + } +} + +fn read_start_event( + r: &mut rxml::Reader, +) -> Result<(rxml::QName, rxml::AttrMap), self::error::Error> { + for ev in r { + match map_nonio_error(ev)? { + rxml::Event::XmlDeclaration(_, rxml::XmlVersion::V1_0) => (), + rxml::Event::StartElement(_, name, attrs) => return Ok((name, attrs)), + _ => { + return Err(self::error::Error::Other( + "Unexpected event at start of document", + )) + } + } + } + Err(self::error::Error::XmlError( + rxml::error::XmlError::InvalidEof("before start of element"), + )) +} + +/// Attempt to parse a type implementing [`FromXml`] from a byte buffer +/// containing XML data. +pub fn from_bytes(mut buf: &[u8]) -> Result { + let mut reader = rxml::Reader::new(&mut buf); + let (name, attrs) = read_start_event(&mut reader)?; + let mut builder = match T::from_events(name, attrs) { + Ok(v) => v, + Err(self::error::FromEventsError::Mismatch { .. }) => { + return Err(self::error::Error::TypeMismatch) + } + Err(self::error::FromEventsError::Invalid(e)) => return Err(e), + }; + for ev in reader { + match builder.feed(map_nonio_error(ev)?)? { + Some(v) => return Ok(v), + None => (), + } + } + Err(self::error::Error::XmlError( + rxml::error::XmlError::InvalidEof("while parsing FromXml impl"), + )) +}