sophia_iri/
_wrapper.rs

1//! I provide generic wrappers around `Borrow<str>` types,
2//! guaranteeing that their underlying string is a valid IRI or IRI reference.
3use super::resolve::{BaseIri, BaseIriRef};
4use super::{InvalidIri, IsIri, IsIriRef, *};
5use std::borrow::Borrow;
6use std::fmt::Display;
7
8wrap! { Iri borrowing str :
9    /// This wrapper guarantees that the underlying `str`
10    /// satisfies the `IRI` rule in  RFC-3687
11    /// (i.e. an absolute IRI with an optional fragment).
12    pub fn new(iri: T) -> Result<Self, InvalidIri> {
13        if is_absolute_iri_ref(iri.borrow()) {
14            Ok(Iri(iri))
15        } else {
16            Err(InvalidIri(iri.borrow().to_string()))
17        }
18    }
19
20    /// Gets a reference to the underlying &str.
21    pub fn as_str(&self) -> &str {
22        self.0.borrow()
23    }
24
25    /// Resolve a relative IRI reference against this one.
26    ///
27    /// NB: when resolving multiple IRI references against the same base,
28    /// it is preferable to first turn it into a [`BaseIri`],
29    /// with the [`Iri::as_base`] or [`Iri::to_base`] methods.
30    pub fn resolve<U: IsIriRef>(&self, rel: U) -> Iri<String> {
31        self.as_base().resolve(rel)
32    }
33
34    /// Borrow this IRI as a [`BaseIri`]
35    /// providing more efficient and flexible resolution methods than [`Iri::resolve`].
36    pub fn as_base(&self) -> BaseIri<&str> {
37        BaseIri::new(self.0.borrow()).unwrap()
38    }
39
40    /// Turn this IRI into a [`BaseIri`]
41    /// providing more efficient and flexible resolution methods than [`Iri::resolve`].
42    pub fn to_base(self) -> BaseIri<T>
43    where
44        T: std::ops::Deref<Target = str>,
45    {
46        BaseIri::new(self.0).unwrap()
47    }
48}
49
50impl<T: Borrow<str>> IsIriRef for Iri<T> {}
51impl<T: Borrow<str>> IsIri for Iri<T> {}
52
53impl<T: Borrow<str>> Display for Iri<T> {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        write!(f, "{}", self.0.borrow())
56    }
57}
58
59//
60
61wrap! { IriRef borrowing str :
62    /// This wrapper guarantees that the underlying `str`
63    /// satisfies the `irelative-ref` rule in  RFC-3687
64    /// (i.e. an absolute or relative IRI reference).
65    pub fn new(iri: T) -> Result<Self, InvalidIri> {
66        if is_valid_iri_ref(iri.borrow()) {
67            Ok(IriRef(iri))
68        } else {
69            Err(InvalidIri(iri.borrow().to_string()))
70        }
71    }
72
73    /// Gets a reference to the underlying &str.
74    pub fn as_str(&self) -> &str {
75        self.0.borrow()
76    }
77
78    /// Resolve a relative IRI reference against this one.
79    ///
80    /// NB: when resolving multiple IRI references against the same base,
81    /// it is preferable to first turn it into a [`BaseIriRef`],
82    /// with the [`IriRef::as_base`] or [`IriRef::to_base`] methods.
83    pub fn resolve<U: IsIriRef>(&self, rel: U) -> IriRef<String> {
84        self.as_base().resolve(rel)
85    }
86
87    /// Borrow this IRI as a [`BaseIriRef`]
88    /// providing more efficient and flexible resolution methods than [`IriRef::resolve`].
89    pub fn as_base(&self) -> BaseIriRef<&str> {
90        BaseIriRef::new(self.0.borrow()).unwrap()
91    }
92
93    /// Turn this IRI into a [`BaseIriRef`]
94    /// providing more efficient and flexible resolution methods than [`IriRef::resolve`].
95    pub fn to_base(self) -> BaseIriRef<T>
96    where
97        T: std::ops::Deref<Target = str>,
98    {
99        BaseIriRef::new(self.0).unwrap()
100    }
101}
102
103impl<T: Borrow<str>> IsIriRef for IriRef<T> {}
104
105impl<T: Borrow<str>> Display for IriRef<T> {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}", self.0.borrow())
108    }
109}
110
111//
112
113#[cfg(test)]
114mod test {
115    use super::*;
116    use crate::test::*;
117
118    #[test]
119    fn iri() {
120        for (txt, (abs, ..)) in POSITIVE_IRIS {
121            assert!(Iri::new(*txt).is_ok() == *abs)
122        }
123        for txt in NEGATIVE_IRIS {
124            assert!(Iri::new(*txt).is_err())
125        }
126    }
127
128    #[test]
129    fn iri_box() {
130        for (txt, (abs, ..)) in POSITIVE_IRIS {
131            assert!(Iri::new(Box::from(txt as &str)).is_ok() == *abs)
132        }
133        for txt in NEGATIVE_IRIS {
134            assert!(Iri::new(Box::from(txt as &str)).is_err())
135        }
136    }
137
138    #[test]
139    fn iri_ref() {
140        for (txt, _) in POSITIVE_IRIS {
141            assert!(IriRef::new(*txt).is_ok())
142        }
143        for txt in NEGATIVE_IRIS {
144            assert!(IriRef::new(*txt).is_err())
145        }
146        for (txt, _) in RELATIVE_IRIS {
147            assert!(IriRef::new(*txt).is_ok())
148        }
149    }
150
151    #[test]
152    fn iri_ref_box() {
153        for (txt, _) in POSITIVE_IRIS {
154            assert!(IriRef::new(Box::from(txt as &str)).is_ok())
155        }
156        for txt in NEGATIVE_IRIS {
157            assert!(IriRef::new(Box::from(txt as &str)).is_err())
158        }
159        for (txt, _) in RELATIVE_IRIS {
160            assert!(IriRef::new(Box::from(txt as &str)).is_ok())
161        }
162    }
163
164    #[test]
165    fn heterogeneous_comparison() {
166        let iri1 = Iri::new(String::from("http://example.com/")).unwrap();
167        let iri2 = Iri::new_unchecked(iri1.as_str());
168        assert_eq!(iri1, iri2);
169    }
170}