Skip to main content

perma_core/
uri.rs

1use core::fmt::{self, Display};
2use core::net::IpAddr;
3use core::ops::{Deref, DerefMut};
4
5use serde::de::Error;
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8pub use url::{Host, ParseError as UriParseError, Position, Url as Uri};
9
10#[repr(transparent)]
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct RelativeRef(Uri);
13
14impl RelativeRef {
15    const FAKE_SCHEME: &'static str = "base";
16    const FAKE_RELATIVE_HOST: &'static str = ".";
17
18    pub fn parse(input: &str) -> Result<Self, UriParseError> {
19        if input.starts_with('/') {
20            Uri::parse(&format!("{}://{}", Self::FAKE_SCHEME, input)).map(Self)
21        } else {
22            Uri::parse(&format!(
23                "{}://{}/{}",
24                Self::FAKE_SCHEME,
25                Self::FAKE_RELATIVE_HOST,
26                input,
27            ))
28            .map(Self)
29        }
30    }
31
32    pub fn as_str(&self) -> &str {
33        match self.0.domain() {
34            None => &self.0[Position::BeforePath..],
35            Some(".") => self.0[Position::BeforePath..].trim_start_matches('/'),
36            _ => unreachable!(),
37        }
38    }
39
40    pub fn path(&self) -> &str {
41        match self.0.domain() {
42            None => self.0.path(),
43            Some(".") => self.0.path().trim_start_matches('/'),
44            _ => unreachable!(),
45        }
46    }
47
48    pub fn join(&self, input: &str) -> Result<Self, UriParseError> {
49        self.0.join(input).map(Self)
50    }
51
52    pub fn set_ip_host(&mut self, address: IpAddr) {
53        let _ = address;
54    }
55
56    pub fn set_host(&mut self, host: &str) {
57        let _ = host;
58    }
59
60    pub fn authority(&self) -> &str {
61        ""
62    }
63
64    pub fn scheme(&self) -> &str {
65        ""
66    }
67
68    pub fn host(&self) -> Option<Host<&str>> {
69        None
70    }
71
72    pub fn host_str(&self) -> Option<&str> {
73        None
74    }
75
76    pub fn has_host(&self) -> bool {
77        false
78    }
79
80    pub fn domain(&self) -> Option<&str> {
81        None
82    }
83}
84
85impl Deref for RelativeRef {
86    type Target = Uri;
87
88    fn deref(&self) -> &Self::Target {
89        &self.0
90    }
91}
92
93impl DerefMut for RelativeRef {
94    fn deref_mut(&mut self) -> &mut Self::Target {
95        &mut self.0
96    }
97}
98
99impl Display for RelativeRef {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        self.as_str().fmt(f)
102    }
103}
104
105impl Serialize for RelativeRef {
106    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
107        self.as_str().serialize(serializer)
108    }
109}
110
111impl<'de> Deserialize<'de> for RelativeRef {
112    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113    where
114        D: Deserializer<'de>,
115    {
116        let s = String::deserialize(deserializer)?;
117        Self::parse(&s).map_err(D::Error::custom)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn simple_reflexivity() {
127        let paths = [
128            "abc",
129            "/abc",
130            // FIXME: dot-segments are not preserved
131            // "../abc",
132            // "/abc/../def",
133            "abc/def",
134            "abc/def/",
135            "abc/def/*",
136            "abc?q=1#title",
137        ];
138
139        for path in paths {
140            assert_eq!(path, RelativeRef::parse(path).unwrap().to_string());
141        }
142    }
143}