Skip to main content

iri_rs_core/
error.rs

1use std::{error::Error, fmt, net::AddrParseError};
2
3#[derive(Debug)]
4pub struct IriParseError {
5    pub(crate) kind: IriParseErrorKind,
6}
7
8impl IriParseError {
9    pub fn kind(&self) -> &IriParseErrorKind {
10        &self.kind
11    }
12}
13
14impl fmt::Display for IriParseError {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        match &self.kind {
17            IriParseErrorKind::NoScheme => write!(f, "No scheme found in an absolute IRI"),
18            IriParseErrorKind::EmptyScheme => write!(f, "Empty schemes are not allowed"),
19            IriParseErrorKind::InvalidSchemeCharacter(c) => {
20                write!(f, "Invalid character '{c}' in scheme")
21            }
22            IriParseErrorKind::InvalidHostCharacter(c) => {
23                write!(f, "Invalid character '{c}' in host")
24            }
25            IriParseErrorKind::UnmatchedHostBracket => {
26                write!(f, "'[' bracket must be paired with a closing one in host")
27            }
28            IriParseErrorKind::InvalidHostIp(e) => write!(f, "Invalid host IP ({e})"),
29            IriParseErrorKind::InvalidPortCharacter(c) => write!(f, "Invalid character '{c}'"),
30            IriParseErrorKind::InvalidIriCodePoint(c) => {
31                write!(f, "Invalid IRI code point '{c}'")
32            }
33            IriParseErrorKind::InvalidPercentEncoding(cs) => {
34                write!(
35                    f,
36                    "Invalid IRI percent encoding '{}'",
37                    cs.iter().flatten().collect::<String>()
38                )
39            }
40            IriParseErrorKind::PathStartingWithTwoSlashes => {
41                write!(f, "An IRI path is not allowed to start with //")
42            }
43            IriParseErrorKind::NonAsciiInUri => write!(f, "Non-ASCII character in URI"),
44        }
45    }
46}
47
48impl Error for IriParseError {
49    fn source(&self) -> Option<&(dyn Error + 'static)> {
50        if let IriParseErrorKind::InvalidHostIp(e) = &self.kind {
51            Some(e)
52        } else {
53            None
54        }
55    }
56}
57
58impl From<IriParseErrorKind> for IriParseError {
59    fn from(kind: IriParseErrorKind) -> Self {
60        Self { kind }
61    }
62}
63
64#[derive(Debug)]
65pub enum IriParseErrorKind {
66    NoScheme,
67    EmptyScheme,
68    InvalidSchemeCharacter(char),
69    UnmatchedHostBracket,
70    InvalidHostCharacter(char),
71    InvalidHostIp(AddrParseError),
72    InvalidPortCharacter(char),
73    InvalidIriCodePoint(char),
74    InvalidPercentEncoding([Option<char>; 3]),
75    PathStartingWithTwoSlashes,
76    NonAsciiInUri,
77}
78
79#[derive(Debug, thiserror::Error)]
80#[error("invalid IRI")]
81pub struct InvalidIri<T>(pub T);
82
83#[derive(Debug, thiserror::Error)]
84#[error("invalid IRI reference")]
85pub struct InvalidIriRef<T>(pub T);
86
87#[derive(Debug, thiserror::Error)]
88#[error("invalid URI")]
89pub struct InvalidUri<T>(pub T);
90
91#[derive(Debug, thiserror::Error)]
92#[error("invalid URI reference")]
93pub struct InvalidUriRef<T>(pub T);
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn variants_construct() {
101        let _ = IriParseError::from(IriParseErrorKind::NoScheme);
102        let _ = IriParseError::from(IriParseErrorKind::EmptyScheme);
103        let _ = IriParseError::from(IriParseErrorKind::InvalidSchemeCharacter('!'));
104        let _ = IriParseError::from(IriParseErrorKind::UnmatchedHostBracket);
105        let _ = IriParseError::from(IriParseErrorKind::InvalidHostCharacter(' '));
106        let _ = IriParseError::from(IriParseErrorKind::InvalidPortCharacter('a'));
107        let _ = IriParseError::from(IriParseErrorKind::InvalidIriCodePoint('\0'));
108        let _ = IriParseError::from(IriParseErrorKind::InvalidPercentEncoding([Some('%'), None, None]));
109        let _ = IriParseError::from(IriParseErrorKind::PathStartingWithTwoSlashes);
110        let _ = IriParseError::from(IriParseErrorKind::NonAsciiInUri);
111        let _: InvalidIri<&str> = InvalidIri("x");
112        let _: InvalidIriRef<&str> = InvalidIriRef("x");
113        let _: InvalidUri<&str> = InvalidUri("x");
114        let _: InvalidUriRef<&str> = InvalidUriRef("x");
115    }
116}