1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! I provide generic wrappers around `Borrow<str>` types,
//! guaranteeing that their underlying string is a valid IRI or IRI reference.
use super::resolve::{BaseIri, BaseIriRef};
use super::{InvalidIri, IsIri, IsIriRef, *};
use std::borrow::Borrow;
use std::fmt::Display;

wrap! { Iri borrowing str :
    /// This wrapper guarantees that the underlying `str`
    /// satisfies the `IRI` rule in  RFC-3687
    /// (i.e. an absolute IRI with an optional fragment).
    pub fn new(iri: T) -> Result<Self, InvalidIri> {
        if is_absolute_iri_ref(iri.borrow()) {
            Ok(Iri(iri))
        } else {
            Err(InvalidIri(iri.borrow().to_string()))
        }
    }

    /// Gets a reference to the underlying &str.
    pub fn as_str(&self) -> &str {
        self.0.borrow()
    }

    /// Resolve a relative IRI reference against this one.
    ///
    /// NB: when resolving multiple IRI references against the same base,
    /// it is preferable to first turn it into a [`BaseIri`],
    /// with the [`Iri::as_base`] or [`Iri::to_base`] methods.
    pub fn resolve<U: IsIriRef>(&self, rel: U) -> Iri<String> {
        self.as_base().resolve(rel)
    }

    /// Borrow this IRI as a [`BaseIri`]
    /// providing more efficient and flexible resolution methods than [`Iri::resolve`].
    pub fn as_base(&self) -> BaseIri<&str> {
        BaseIri::new(self.0.borrow()).unwrap()
    }

    /// Turn this IRI into a [`BaseIri`]
    /// providing more efficient and flexible resolution methods than [`Iri::resolve`].
    pub fn to_base(self) -> BaseIri<T>
    where
        T: std::ops::Deref<Target = str>,
    {
        BaseIri::new(self.0).unwrap()
    }
}

impl<T: Borrow<str>> IsIriRef for Iri<T> {}
impl<T: Borrow<str>> IsIri for Iri<T> {}

impl<T: Borrow<str>> Display for Iri<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0.borrow())
    }
}

//

wrap! { IriRef borrowing str :
    /// This wrapper guarantees that the underlying `str`
    /// satisfies the `irelative-ref` rule in  RFC-3687
    /// (i.e. an absolute or relative IRI reference).
    pub fn new(iri: T) -> Result<Self, InvalidIri> {
        if is_valid_iri_ref(iri.borrow()) {
            Ok(IriRef(iri))
        } else {
            Err(InvalidIri(iri.borrow().to_string()))
        }
    }

    /// Gets a reference to the underlying &str.
    pub fn as_str(&self) -> &str {
        self.0.borrow()
    }

    /// Resolve a relative IRI reference against this one.
    ///
    /// NB: when resolving multiple IRI references against the same base,
    /// it is preferable to first turn it into a [`BaseIriRef`],
    /// with the [`IriRef::as_base`] or [`IriRef::to_base`] methods.
    pub fn resolve<U: IsIriRef>(&self, rel: U) -> IriRef<String> {
        self.as_base().resolve(rel)
    }

    /// Borrow this IRI as a [`BaseIriRef`]
    /// providing more efficient and flexible resolution methods than [`IriRef::resolve`].
    pub fn as_base(&self) -> BaseIriRef<&str> {
        BaseIriRef::new(self.0.borrow()).unwrap()
    }

    /// Turn this IRI into a [`BaseIriRef`]
    /// providing more efficient and flexible resolution methods than [`IriRef::resolve`].
    pub fn to_base(self) -> BaseIriRef<T>
    where
        T: std::ops::Deref<Target = str>,
    {
        BaseIriRef::new(self.0).unwrap()
    }
}

impl<T: Borrow<str>> IsIriRef for IriRef<T> {}

impl<T: Borrow<str>> Display for IriRef<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0.borrow())
    }
}

//

#[cfg(test)]
mod test {
    use super::*;
    use crate::test::*;

    #[test]
    fn iri() {
        for (txt, (abs, ..)) in POSITIVE_IRIS {
            assert!(Iri::new(*txt).is_ok() == *abs)
        }
        for txt in NEGATIVE_IRIS {
            assert!(Iri::new(*txt).is_err())
        }
    }

    #[test]
    fn iri_box() {
        for (txt, (abs, ..)) in POSITIVE_IRIS {
            assert!(Iri::new(Box::from(txt as &str)).is_ok() == *abs)
        }
        for txt in NEGATIVE_IRIS {
            assert!(Iri::new(Box::from(txt as &str)).is_err())
        }
    }

    #[test]
    fn iri_ref() {
        for (txt, _) in POSITIVE_IRIS {
            assert!(IriRef::new(*txt).is_ok())
        }
        for txt in NEGATIVE_IRIS {
            assert!(IriRef::new(*txt).is_err())
        }
        for (txt, _) in RELATIVE_IRIS {
            assert!(IriRef::new(*txt).is_ok())
        }
    }

    #[test]
    fn iri_ref_box() {
        for (txt, _) in POSITIVE_IRIS {
            assert!(IriRef::new(Box::from(txt as &str)).is_ok())
        }
        for txt in NEGATIVE_IRIS {
            assert!(IriRef::new(Box::from(txt as &str)).is_err())
        }
        for (txt, _) in RELATIVE_IRIS {
            assert!(IriRef::new(Box::from(txt as &str)).is_ok())
        }
    }

    #[test]
    fn heterogeneous_comparison() {
        let iri1 = Iri::new(String::from("http://example.com/")).unwrap();
        let iri2 = Iri::new_unchecked(iri1.as_str());
        assert_eq!(iri1, iri2);
    }
}