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}