Skip to main content

oxrdf/
named_node.rs

1use oxiri::{Iri, IriParseError};
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
4use std::cmp::Ordering;
5use std::fmt;
6
7/// An owned RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri).
8///
9/// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation:
10/// ```
11/// use oxrdf::NamedNode;
12///
13/// assert_eq!(
14///     "<http://example.com/foo>",
15///     NamedNode::new("http://example.com/foo")?.to_string()
16/// );
17/// # Result::<_,oxrdf::IriParseError>::Ok(())
18/// ```
19#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
20pub struct NamedNode {
21    iri: String,
22}
23
24impl NamedNode {
25    /// Builds and validate an RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri).
26    pub fn new(iri: impl Into<String>) -> Result<Self, IriParseError> {
27        Ok(Self::new_from_iri(Iri::parse(iri.into())?))
28    }
29
30    #[inline]
31    pub(crate) fn new_from_iri(iri: Iri<String>) -> Self {
32        Self::new_unchecked(iri.into_inner())
33    }
34
35    /// Builds an RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) from a string.
36    ///
37    /// It is the caller's responsibility to ensure that `iri` is a valid IRI.
38    ///
39    /// [`NamedNode::new()`] is a safe version of this constructor and should be used for untrusted data.
40    #[inline]
41    pub fn new_unchecked(iri: impl Into<String>) -> Self {
42        Self { iri: iri.into() }
43    }
44
45    #[inline]
46    pub fn as_str(&self) -> &str {
47        self.iri.as_str()
48    }
49
50    #[inline]
51    pub fn into_string(self) -> String {
52        self.iri
53    }
54
55    #[inline]
56    pub fn as_ref(&self) -> NamedNodeRef<'_> {
57        NamedNodeRef::new_unchecked(&self.iri)
58    }
59}
60
61impl fmt::Display for NamedNode {
62    #[inline]
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        self.as_ref().fmt(f)
65    }
66}
67
68impl PartialEq<str> for NamedNode {
69    #[inline]
70    fn eq(&self, other: &str) -> bool {
71        self.as_str() == other
72    }
73}
74
75impl PartialEq<NamedNode> for str {
76    #[inline]
77    fn eq(&self, other: &NamedNode) -> bool {
78        self == other.as_str()
79    }
80}
81
82impl PartialEq<&str> for NamedNode {
83    #[inline]
84    fn eq(&self, other: &&str) -> bool {
85        self == *other
86    }
87}
88
89impl PartialEq<NamedNode> for &str {
90    #[inline]
91    fn eq(&self, other: &NamedNode) -> bool {
92        *self == other
93    }
94}
95
96/// A borrowed RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri).
97///
98/// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation:
99/// ```
100/// use oxrdf::NamedNodeRef;
101///
102/// assert_eq!(
103///     "<http://example.com/foo>",
104///     NamedNodeRef::new("http://example.com/foo")?.to_string()
105/// );
106/// # Result::<_,oxrdf::IriParseError>::Ok(())
107/// ```
108#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
109pub struct NamedNodeRef<'a> {
110    iri: &'a str,
111}
112
113impl<'a> NamedNodeRef<'a> {
114    /// Builds and validate an RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri)
115    pub fn new(iri: &'a str) -> Result<Self, IriParseError> {
116        Ok(Self::new_from_iri(Iri::parse(iri)?))
117    }
118
119    #[inline]
120    pub(crate) fn new_from_iri(iri: Iri<&'a str>) -> Self {
121        Self::new_unchecked(iri.into_inner())
122    }
123
124    /// Builds an RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) from a string.
125    ///
126    /// It is the caller's responsibility to ensure that `iri` is a valid IRI.
127    ///
128    /// [`NamedNode::new()`] is a safe version of this constructor and should be used for untrusted data.
129    #[inline]
130    pub const fn new_unchecked(iri: &'a str) -> Self {
131        Self { iri }
132    }
133
134    #[inline]
135    pub const fn as_str(self) -> &'a str {
136        self.iri
137    }
138
139    #[inline]
140    pub fn into_owned(self) -> NamedNode {
141        NamedNode::new_unchecked(self.iri)
142    }
143}
144
145impl fmt::Display for NamedNodeRef<'_> {
146    #[inline]
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        write!(f, "<{}>", self.as_str())
149    }
150}
151
152impl From<NamedNodeRef<'_>> for NamedNode {
153    #[inline]
154    fn from(node: NamedNodeRef<'_>) -> Self {
155        node.into_owned()
156    }
157}
158
159impl<'a> From<&'a NamedNode> for NamedNodeRef<'a> {
160    #[inline]
161    fn from(node: &'a NamedNode) -> Self {
162        node.as_ref()
163    }
164}
165
166impl PartialEq<NamedNode> for NamedNodeRef<'_> {
167    #[inline]
168    fn eq(&self, other: &NamedNode) -> bool {
169        self.as_str() == other.as_str()
170    }
171}
172
173impl PartialEq<NamedNodeRef<'_>> for NamedNode {
174    #[inline]
175    fn eq(&self, other: &NamedNodeRef<'_>) -> bool {
176        self.as_str() == other.as_str()
177    }
178}
179
180impl PartialEq<str> for NamedNodeRef<'_> {
181    #[inline]
182    fn eq(&self, other: &str) -> bool {
183        self.as_str() == other
184    }
185}
186
187impl PartialEq<NamedNodeRef<'_>> for str {
188    #[inline]
189    fn eq(&self, other: &NamedNodeRef<'_>) -> bool {
190        self == other.as_str()
191    }
192}
193
194impl PartialEq<&str> for NamedNodeRef<'_> {
195    #[inline]
196    fn eq(&self, other: &&str) -> bool {
197        self == *other
198    }
199}
200
201impl PartialEq<NamedNodeRef<'_>> for &str {
202    #[inline]
203    fn eq(&self, other: &NamedNodeRef<'_>) -> bool {
204        *self == other
205    }
206}
207
208impl PartialOrd<NamedNode> for NamedNodeRef<'_> {
209    #[inline]
210    fn partial_cmp(&self, other: &NamedNode) -> Option<Ordering> {
211        self.partial_cmp(&other.as_ref())
212    }
213}
214
215impl PartialOrd<NamedNodeRef<'_>> for NamedNode {
216    #[inline]
217    fn partial_cmp(&self, other: &NamedNodeRef<'_>) -> Option<Ordering> {
218        self.as_ref().partial_cmp(other)
219    }
220}
221
222impl From<Iri<String>> for NamedNode {
223    #[inline]
224    fn from(iri: Iri<String>) -> Self {
225        Self {
226            iri: iri.into_inner(),
227        }
228    }
229}
230
231impl<'a> From<Iri<&'a str>> for NamedNodeRef<'a> {
232    #[inline]
233    fn from(iri: Iri<&'a str>) -> Self {
234        Self {
235            iri: iri.into_inner(),
236        }
237    }
238}
239
240#[cfg(feature = "serde")]
241impl Serialize for NamedNode {
242    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
243        self.as_ref().serialize(serializer)
244    }
245}
246
247#[cfg(feature = "serde")]
248impl Serialize for NamedNodeRef<'_> {
249    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
250        #[derive(Serialize)]
251        #[serde(rename = "NamedNode")]
252        struct Value<'a> {
253            value: &'a str,
254        }
255        Value {
256            value: self.as_str(),
257        }
258        .serialize(serializer)
259    }
260}
261
262#[cfg(feature = "serde")]
263impl<'de> Deserialize<'de> for NamedNode {
264    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
265    where
266        D: Deserializer<'de>,
267    {
268        #[derive(Deserialize)]
269        #[serde(rename = "NamedNode")]
270        struct Value {
271            value: String,
272        }
273        Self::new(Value::deserialize(deserializer)?.value).map_err(de::Error::custom)
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280
281    #[test]
282    fn named_node_construction() {
283        assert_eq!(
284            "http://example.org/",
285            NamedNode::new("http://example.org/").unwrap().iri
286        );
287    }
288
289    #[test]
290    #[cfg(feature = "serde")]
291    fn test_serde() {
292        let n = NamedNode::new("http://example.com/foo").unwrap();
293        let json = serde_json::to_string(&n).unwrap();
294        assert_eq!(json, "{\"value\":\"http://example.com/foo\"}");
295        let n2: NamedNode = serde_json::from_str(&json).unwrap();
296        assert_eq!(n2, n);
297    }
298}