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