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