1use super::{Iri, IriRef, IsIri, IsIriRef};
12use std::borrow::Borrow;
13use std::ops::Deref;
14
15pub use oxiri::IriParseError;
16pub use oxiri::{Iri as Oxiri, IriRef as OxiriRef};
17
18#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
22pub struct BaseIri<T>(Oxiri<T>);
23
24impl<T: Deref<Target = str>> IsIriRef for BaseIri<T> {}
25impl<T: Deref<Target = str>> IsIri for BaseIri<T> {}
26
27impl<T: Deref<Target = str>> BaseIri<T> {
28 pub fn new(iri: T) -> Result<Self, IriParseError> {
31 Oxiri::parse(iri).map(BaseIri)
32 }
33
34 pub fn resolve<R: Resolvable<String>>(&self, iri: R) -> R::OutputAbs {
36 R::output_abs(self.0.resolve(iri.borrow()).map(Oxiri::into_inner))
37 }
38
39 pub fn resolve_into<'a, R: Resolvable<&'a str>>(
41 &self,
42 iri: R,
43 buf: &'a mut String,
44 ) -> R::OutputAbs {
45 R::output_abs(self.0.resolve_into(iri.borrow(), buf).map(|_| &buf[..]))
46 }
47}
48
49impl<T: Deref<Target = str>> Borrow<str> for BaseIri<T> {
50 fn borrow(&self) -> &str {
51 &self.0
52 }
53}
54
55impl<T: Deref<Target = str>> Deref for BaseIri<T> {
56 type Target = Oxiri<T>;
57 fn deref(&self) -> &Oxiri<T> {
58 &self.0
59 }
60}
61
62#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
69pub struct BaseIriRef<T>(OxiriRef<T>);
70
71impl<T: Deref<Target = str>> IsIriRef for BaseIriRef<T> {}
72
73impl<T: Deref<Target = str>> BaseIriRef<T> {
74 pub fn new(iri: T) -> Result<Self, IriParseError> {
77 OxiriRef::parse(iri).map(BaseIriRef)
78 }
79
80 pub fn resolve<R: Resolvable<String>>(&self, iri: R) -> R::OutputRel {
82 R::output_rel(self.0.resolve(iri.borrow()).map(OxiriRef::into_inner))
83 }
84
85 pub fn resolve_into<'a, R: Resolvable<&'a str>>(
87 &self,
88 iri: R,
89 buf: &'a mut String,
90 ) -> R::OutputRel {
91 R::output_rel(self.0.resolve_into(iri.borrow(), buf).map(|_| &buf[..]))
92 }
93
94 pub fn to_base_iri(self) -> BaseIri<T> {
99 assert!(self.is_absolute());
100 BaseIri(Oxiri::try_from(self.0).unwrap())
101 }
102}
103
104impl<T: Deref<Target = str>> Borrow<str> for BaseIriRef<T> {
105 fn borrow(&self) -> &str {
106 &self.0
107 }
108}
109
110impl<T: Deref<Target = str>> Deref for BaseIriRef<T> {
111 type Target = OxiriRef<T>;
112 fn deref(&self) -> &OxiriRef<T> {
113 &self.0
114 }
115}
116
117pub trait Resolvable<T: Borrow<str>>: Borrow<str> {
122 type OutputAbs;
124 type OutputRel;
126 fn output_abs(res: Result<T, IriParseError>) -> Self::OutputAbs;
128 fn output_rel(res: Result<T, IriParseError>) -> Self::OutputRel;
130}
131
132impl<T: Borrow<str>> Resolvable<T> for &str {
133 type OutputAbs = Result<Iri<T>, IriParseError>;
134 type OutputRel = Result<IriRef<T>, IriParseError>;
135 fn output_abs(res: Result<T, IriParseError>) -> Self::OutputAbs {
136 res.map(|iri| Iri::new_unchecked(iri))
137 }
138 fn output_rel(res: Result<T, IriParseError>) -> Self::OutputRel {
139 res.map(|iri| IriRef::new_unchecked(iri))
140 }
141}
142
143impl<T: Borrow<str>, U: IsIriRef> Resolvable<T> for U {
144 type OutputAbs = Iri<T>;
145 type OutputRel = IriRef<T>;
146 fn output_abs(res: Result<T, IriParseError>) -> Self::OutputAbs {
147 Iri::new_unchecked(res.unwrap())
148 }
149 fn output_rel(res: Result<T, IriParseError>) -> Self::OutputRel {
150 IriRef::new_unchecked(res.unwrap())
151 }
152}
153
154#[cfg(test)]
157mod test {
158 use super::*;
159 use crate::test::*;
160 use crate::AsIriRef;
161
162 #[test]
163 fn positive() {
164 for (txt, parsed) in POSITIVE_IRIS {
165 let bir = BaseIriRef::new(*txt).unwrap();
166 assert_eq!(bir.is_absolute(), parsed.0);
167 assert_eq!(bir.scheme(), parsed.1);
168 assert_eq!(bir.authority(), parsed.2);
169 assert_eq!(bir.path(), parsed.3);
170 assert_eq!(bir.query(), parsed.4);
171 assert_eq!(bir.fragment(), parsed.5);
172 assert_eq!(bir.to_string(), *txt);
173
174 assert_eq!(bir, IriRef::new(*txt).unwrap().to_base());
175 assert_eq!(bir, IriRef::new(*txt).unwrap().as_base());
176
177 let rbi = BaseIri::new(*txt);
178 if parsed.0 {
179 assert!(rbi.is_ok(), "<{}> → {:?}", txt, rbi);
180 let bi = rbi.unwrap();
181 assert_eq!(bi.scheme(), parsed.1.unwrap());
182 assert_eq!(bi.authority(), parsed.2);
183 assert_eq!(bi.path(), parsed.3);
184 assert_eq!(bi.query(), parsed.4);
185 assert_eq!(bi.fragment(), parsed.5);
186 assert_eq!(bi.to_string(), *txt);
187
188 assert_eq!(bi.as_iri_ref(), bir.as_iri_ref());
189 assert_eq!(bi, Iri::new(*txt).unwrap().to_base());
190 assert_eq!(bi, Iri::new(*txt).unwrap().as_base());
191 } else {
192 assert!(rbi.is_err(), "<{}> → {:?}", txt, rbi);
193 }
194 }
195 }
196
197 #[test]
198 fn negative() {
199 for txt in NEGATIVE_IRIS {
200 let rpir = BaseIriRef::new(*txt);
201 assert!(rpir.is_err(), "<{}> → {:?}", txt, rpir);
202 let rpi = BaseIri::new(*txt);
203 assert!(rpi.is_err(), "<{}> → {:?}", txt, rpi);
204 }
205 }
206
207 #[test]
208 fn relative() {
209 for (rel, abs) in RELATIVE_IRIS {
210 let rbir = BaseIriRef::new(*rel);
211 assert!(rbir.is_ok(), "<{}> → {:?}", rel, rbir);
212
213 let rbi = BaseIri::new(*rel);
214 if rel != abs {
215 assert!(rbi.is_err(), "<{}> → {:?}", rel, rbi);
216 } else {
217 assert!(rbi.is_ok(), "<{}> → {:?}", rel, rbi);
218 assert_eq!(rbir.unwrap().as_iri_ref(), rbi.unwrap().as_iri_ref());
219 }
220 }
221 }
222
223 #[test]
224 fn resolve_iri_parsed() {
225 let base1 = BaseIriRef::new("http://a/b/c/d;p?q").unwrap();
226 let base2: BaseIri<_> = base1.clone().to_base_iri();
227 let mut buf = String::new();
228 for (rel, abs) in RELATIVE_IRIS {
229 let rel = IriRef::new(*rel).unwrap();
230 let got1a = base1.resolve(rel);
231 assert_eq!(&got1a, *abs);
232 buf.clear();
233 let got1b = base1.resolve_into(rel, &mut buf);
234 assert_eq!(&got1b, *abs);
235 let got2 = base2.resolve(rel);
236 assert_eq!(&got2, *abs);
237 buf.clear();
238 let got2b = base2.resolve_into(rel, &mut buf);
239 assert_eq!(&got2b, *abs);
240 }
241 }
242
243 #[test]
244 fn resolve_str() {
245 let base1 = BaseIriRef::new("http://a/b/c/d;p?q").unwrap();
246 let base2: BaseIri<_> = base1.clone().to_base_iri();
247 let mut buf = String::new();
248 for (rel, abs) in RELATIVE_IRIS {
249 let got1a = base1.resolve(*rel).unwrap();
250 assert_eq!(&got1a, *abs);
251 buf.clear();
252 let got1b = base1.resolve_into(*rel, &mut buf).unwrap();
253 assert_eq!(&got1b, *abs);
254 let got2a = base2.resolve(*rel).unwrap();
255 assert_eq!(&got2a, *abs);
256 buf.clear();
257 let got2b = base2.resolve_into(*rel, &mut buf).unwrap();
258 assert_eq!(&got2b, *abs);
259 }
260 }
261
262 #[test]
263 fn resolve_bad_str() {
264 let base1 = BaseIriRef::new("http://a/b/c/d;p?q").unwrap();
265 let base2: BaseIri<_> = base1.clone().to_base_iri();
266 let mut buf = String::new();
267 for txt in NEGATIVE_IRIS {
268 println!("{}", *txt);
269 assert!(base1.resolve(*txt).is_err());
270 assert!(base2.resolve(*txt).is_err());
271 assert!(base1.resolve_into(*txt, &mut buf).is_err());
272 assert!(base2.resolve_into(*txt, &mut buf).is_err());
273 }
274 }
275}