Skip to main content

activitystreams/primitives/
xsd_any_uri.rs

1/*
2 * This file is part of ActivityStreams.
3 *
4 * Copyright © 2020 Riley Trautman
5 *
6 * ActivityStreams is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ActivityStreams is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with ActivityStreams.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/// The type xsd:anyURI represents a Uniform Resource Identifier (URI) reference.
21///
22/// URIs are used to identify resources, and they may be absolute or relative. Absolute URIs
23/// provide the entire context for locating the resources, such as http://datypic.com/prod.html.
24/// Relative URIs are specified as the difference from a base URI, such as ../prod.html. It is also
25/// possible to specify a fragment identifier, using the # character, such as ../prod.html#shirt.
26///
27/// The three previous examples happen to be HTTP URLs (Uniform Resource Locators), but URIs also
28/// encompass URLs of other schemes (e.g., FTP, gopher, telnet), as well as URNs (Uniform Resource
29/// Names). URIs are not required to be dereferencable; that is, it is not necessary for there to
30/// be a web page at http://datypic.com/prod.html in order for this to be a valid URI.
31///
32/// URIs require that some characters be escaped with their hexadecimal Unicode code point preceded
33/// by the % character. This includes non-ASCII characters and some ASCII characters, namely
34/// control characters, spaces, and the following characters (unless they are used as deliimiters
35/// in the URI): <>#%{}|\^`. For example, ../édition.html must be represented instead as
36/// ../%C3%A9dition.html, with the é escaped as %C3%A9. However, the anyURI type will accept these
37/// characters either escaped or unescaped. With the exception of the characters % and #, it will
38/// assume that unescaped characters are intended to be escaped when used in an actual URI,
39/// although the schema processor will do nothing to alter them. It is valid for an anyURI value to
40/// contain a space, but this practice is strongly discouraged. Spaces should instead be escaped
41/// using %20.
42///
43/// The schema processor is not required to parse the contents of an xsd:anyURI value to determine
44/// whether it is valid according to any particular URI scheme. Since the bare minimum rules for
45/// valid URI references are fairly generic, the schema processor will accept most character
46/// strings, including an empty value. The only values that are not accepted are ones that make
47/// inappropriate use of reserved characters, such as ones that contain multiple # characters or
48/// have % characters that are not followed by two hexadecimal digits.
49///
50/// Note that when relative URI references such as "../prod" are used as values of xsd:anyURI, no
51/// attempt is made to determine or keep track of the base URI to which they may be applied. For
52/// more information on URIs, see RFC 2396, Uniform Resource Identifiers (URI): Generic Syntax.
53#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
54pub struct XsdAnyUri(url::Url);
55
56/// The error type produced when an XsdAnyUri cannot be parsed
57#[derive(Clone, Debug, thiserror::Error)]
58#[error("Could not parse XsdAnyUri")]
59pub struct XsdAnyUriError;
60
61impl XsdAnyUri {
62    /// Borrow the underlying string from the XsdAnyUri
63    pub fn as_str(&self) -> &str {
64        self.as_ref()
65    }
66
67    /// Borrow a `url::Url` from the XsdAnyUri
68    pub fn as_url(&self) -> &url::Url {
69        self.as_ref()
70    }
71
72    /// Mutably borrow a `url::Url` from the XsdAnyUri
73    pub fn as_url_mut(&mut self) -> &mut url::Url {
74        self.as_mut()
75    }
76}
77
78impl From<url::Url> for XsdAnyUri {
79    fn from(u: url::Url) -> Self {
80        XsdAnyUri(u)
81    }
82}
83
84impl From<XsdAnyUri> for url::Url {
85    fn from(u: XsdAnyUri) -> Self {
86        u.0
87    }
88}
89
90impl Default for XsdAnyUri {
91    fn default() -> Self {
92        "data:text/plain,uwu".parse().unwrap()
93    }
94}
95
96impl AsRef<str> for XsdAnyUri {
97    fn as_ref(&self) -> &str {
98        self.0.as_ref()
99    }
100}
101
102impl AsRef<url::Url> for XsdAnyUri {
103    fn as_ref(&self) -> &url::Url {
104        &self.0
105    }
106}
107
108impl AsMut<url::Url> for XsdAnyUri {
109    fn as_mut(&mut self) -> &mut url::Url {
110        &mut self.0
111    }
112}
113
114impl std::convert::TryFrom<String> for XsdAnyUri {
115    type Error = XsdAnyUriError;
116
117    fn try_from(s: String) -> Result<Self, Self::Error> {
118        s.parse()
119    }
120}
121
122impl std::convert::TryFrom<&str> for XsdAnyUri {
123    type Error = XsdAnyUriError;
124
125    fn try_from(s: &str) -> Result<Self, Self::Error> {
126        s.parse()
127    }
128}
129
130impl std::convert::TryFrom<&mut str> for XsdAnyUri {
131    type Error = XsdAnyUriError;
132
133    fn try_from(s: &mut str) -> Result<Self, Self::Error> {
134        s.parse()
135    }
136}
137
138impl std::str::FromStr for XsdAnyUri {
139    type Err = XsdAnyUriError;
140
141    fn from_str(s: &str) -> Result<Self, Self::Err> {
142        Ok(XsdAnyUri(s.parse().map_err(|_| XsdAnyUriError)?))
143    }
144}
145
146impl std::fmt::Display for XsdAnyUri {
147    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
148        std::fmt::Display::fmt(&self.0, f)
149    }
150}
151
152impl serde::ser::Serialize for XsdAnyUri {
153    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154    where
155        S: serde::ser::Serializer,
156    {
157        serializer.serialize_str(&self.0.to_string())
158    }
159}
160
161impl<'de> serde::de::Deserialize<'de> for XsdAnyUri {
162    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
163    where
164        D: serde::de::Deserializer<'de>,
165    {
166        let s = String::deserialize(deserializer)?;
167        s.parse().map_err(serde::de::Error::custom)
168    }
169}