diff options
| author | 2025-02-25 18:08:20 +0000 | |
|---|---|---|
| committer | 2025-02-25 18:08:20 +0000 | |
| commit | fe389c38ca2ff23e3f2bd2305b455e4d551a9ccc (patch) | |
| tree | 11fe43131016adc290a4bc381dd5ede34679dc5b /src | |
| parent | bbb1452905a3f59e178031bb3eeca4d963e50394 (diff) | |
| download | peanuts-fe389c38ca2ff23e3f2bd2305b455e4d551a9ccc.tar.gz peanuts-fe389c38ca2ff23e3f2bd2305b455e4d551a9ccc.tar.bz2 peanuts-fe389c38ca2ff23e3f2bd2305b455e4d551a9ccc.zip  | |
implement proper errors with thiserror
Diffstat (limited to 'src')
| -rw-r--r-- | src/element.rs | 20 | ||||
| -rw-r--r-- | src/error.rs | 85 | ||||
| -rw-r--r-- | src/xml/mod.rs | 16 | 
3 files changed, 64 insertions, 57 deletions
diff --git a/src/element.rs b/src/element.rs index 48d4d90..b954e2d 100644 --- a/src/element.rs +++ b/src/element.rs @@ -89,9 +89,10 @@ impl Element {          if self.name.local_name == name {              Ok(())          } else { -            return Err(DeserializeError::IncorrectName( -                self.name.local_name.clone(), -            )); +            return Err(DeserializeError::IncorrectName { +                expected: name.to_string(), +                found: self.name.local_name.clone(), +            });          }      } @@ -99,10 +100,15 @@ impl Element {          if self.name.namespace.as_deref() == Some(namespace) {              return Ok(());          } else { -            if let Some(namespace) = &self.name.namespace { -                return Err(DeserializeError::IncorrectNamespace(namespace.clone())); +            if let Some(actual_namespace) = &self.name.namespace { +                return Err(DeserializeError::IncorrectNamespace { +                    expected: namespace.to_string(), +                    found: actual_namespace.clone(), +                });              } else { -                return Err(DeserializeError::Unqualified); +                return Err(DeserializeError::Unqualified { +                    expected: namespace.to_string(), +                });              }          }      } @@ -358,7 +364,7 @@ impl Element {              let child = self                  .content                  .pop_front() -                .ok_or(DeserializeError::MissingChild)?; +                .ok_or(DeserializeError::MissingValue)?;              match child {                  Content::Element(_) => {                      return Err(DeserializeError::UnexpectedContent(self.content.clone())) diff --git a/src/error.rs b/src/error.rs index cf01895..65a1503 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,71 +1,72 @@  use std::{      collections::{HashMap, VecDeque},      num::ParseIntError, -    str::{FromStr, Utf8Error}, +    str::Utf8Error,  }; -use crate::{ -    element::{Content, Name, NamespaceDeclaration}, -    Element, -}; +use thiserror::Error; + +use crate::element::{Content, Name, NamespaceDeclaration}; -#[derive(Debug)] +#[derive(Error, Debug)]  pub enum DeserializeError { +    #[error("could not parse string {0:?} to requested value")]      FromStr(String), +    #[error("unexpected attributes {0:?}")]      UnexpectedAttributes(HashMap<Name, String>), +    #[error("unexpected element content: {0:?}")]      UnexpectedContent(VecDeque<Content>), -    UnexpectedElement(Element), +    #[error("attribute `{0:?}` missing")]      MissingAttribute(Name), -    IncorrectName(String), -    IncorrectNamespace(String), -    Unqualified, +    #[error("incorrect localname: expected `{expected:?}`, found `{found:?}`")] +    IncorrectName { expected: String, found: String }, +    #[error("incorrect namespace: expected `{expected:?}`, found `{found:?}`")] +    IncorrectNamespace { expected: String, found: String }, +    #[error("unqualified namespace: expected `{expected:?}`")] +    Unqualified { expected: String }, +    #[error("element missing expected child")]      MissingChild, +    #[error("element missing expected text value")]      MissingValue,  } -#[derive(Debug)] -// TODO: thiserror +#[derive(Error, Debug)]  pub enum Error { -    ReadError(std::io::Error), -    Utf8Error(Utf8Error), +    #[error(transparent)] +    ReadError(#[from] std::io::Error), +    #[error(transparent)] +    Utf8Error(#[from] Utf8Error), +    #[error("nom parse error: {0}")]      ParseError(String), +    #[error("unknown xml entity reference `&{0};`")]      EntityProcessError(String), -    // TODO: better choice for failures than string -    InvalidCharRef(String), +    #[error(transparent)] +    InvalidCharRef(CharRefError), +    #[error("duplicate namespace declaration: {0:?}")]      DuplicateNameSpaceDeclaration(NamespaceDeclaration), +    #[error("duplicate attribute: {0}")]      DuplicateAttribute(String), -    UnqualifiedNamespace(String), +    #[error("mismatched end tag: expected `{0:?}`, found `{1:?}`")]      MismatchedEndTag(Name, Name), +    #[error("not currently in any element")]      NotInElement(String), +    #[error("extra unexpected data included in complete parse: `{0}`")]      ExtraData(String), +    #[error("namespace `{0}` has not previously been declared")]      UndeclaredNamespace(String), -    IncorrectName(Name), -    DeserializeError(String), -    Deserialize(DeserializeError), +    #[error(transparent)] +    Deserialize(#[from] DeserializeError),      /// root element end tag already processed +    #[error("root element has already been fully processed")]      RootElementEnded,  } -impl From<DeserializeError> for Error { -    fn from(e: DeserializeError) -> Self { -        Self::Deserialize(e) -    } -} - -impl From<std::io::Error> for Error { -    fn from(e: std::io::Error) -> Self { -        Self::ReadError(e) -    } -} - -impl From<Utf8Error> for Error { -    fn from(e: Utf8Error) -> Self { -        Self::Utf8Error(e) -    } -} - -impl From<ParseIntError> for Error { -    fn from(e: ParseIntError) -> Self { -        Self::InvalidCharRef(e.to_string()) -    } +#[derive(Error, Debug)] +pub enum CharRefError { +    #[error(transparent)] +    ParseInt(#[from] ParseIntError), +    #[error("u32 `{0}` does not represent a valid char")] +    IntegerNotAChar(u32), +    #[error("`{0}` is not a valid xml char")] +    InvalidXMLChar(char),  } diff --git a/src/xml/mod.rs b/src/xml/mod.rs index 43f3027..3982070 100644 --- a/src/xml/mod.rs +++ b/src/xml/mod.rs @@ -2,7 +2,7 @@ use std::{char, convert::Infallible, ops::Deref, str::FromStr};  use parsers_complete::Parser; -use crate::error::Error; +use crate::error::{CharRefError, Error};  pub mod composers;  pub mod parsers; @@ -736,23 +736,23 @@ impl<'s> CharRef<'s> {          let int: u32;          match self {              CharRef::Decimal(dec) => { -                int = dec.parse()?; +                int = dec +                    .parse() +                    .map_err(|e| Error::InvalidCharRef(CharRefError::ParseInt(e)))?;              }              CharRef::Hexadecimal(hex) => { -                int = <u32>::from_str_radix(hex, 16)?; +                int = <u32>::from_str_radix(hex, 16) +                    .map_err(|e| Error::InvalidCharRef(CharRefError::ParseInt(e)))?;              }          }          let c = std::char::from_u32(int); -        let c = c.ok_or_else(|| Error::InvalidCharRef(int.to_string()))?; +        let c = c.ok_or_else(|| Error::InvalidCharRef(CharRefError::IntegerNotAChar(int)))?;          if matches!(c, '\u{9}' | '\u{A}' | '\u{D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..='\u{10FFFF}')          {              return Ok(c);          } else { -            return Err(Error::InvalidCharRef(format!( -                "{} is not a valid xml char", -                c -            ))); +            return Err(Error::InvalidCharRef(CharRefError::InvalidXMLChar(c)));          };      }  }  | 
