Skip to main content

iri_rs_core/
mutate.rs

1//! Mutation methods on owning buffer types (`IriBuf`, `IriRefBuf`, `UriBuf`, `UriRefBuf`).
2
3use crate::{
4    error::{InvalidIri, InvalidUri, IriParseError},
5    parse::find_iri_ref_positions,
6    resolve::resolve,
7    types::{Iri, IriBuf, IriRef, IriRefBuf, Uri, UriBuf, UriRef, UriRefBuf},
8    validate::{
9        validate_authority_body, validate_fragment, validate_iri_ref, validate_path,
10        validate_query, validate_resolved_path, validate_scheme,
11    },
12};
13
14impl IriRefBuf {
15    pub fn set_scheme(&mut self, scheme: Option<&str>) -> Result<(), IriParseError> {
16        if let Some(s) = scheme {
17            validate_scheme(s)?;
18        }
19        rebuild(self, Edit::Scheme(scheme.map(str::to_string)), true)
20    }
21
22    pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
23        if let Some(a) = authority {
24            validate_authority_body(a, true)?;
25        }
26        rebuild(self, Edit::Authority(authority.map(str::to_string)), true)
27    }
28
29    pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
30        validate_path(path, true)?;
31        rebuild(self, Edit::Path(path.to_string()), true)
32    }
33
34    pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
35        if let Some(q) = query {
36            validate_query(q, true)?;
37        }
38        rebuild(self, Edit::Query(query.map(str::to_string)), true)
39    }
40
41    pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
42        if let Some(f) = fragment {
43            validate_fragment(f, true)?;
44        }
45        rebuild(self, Edit::Fragment(fragment.map(str::to_string)), true)
46    }
47
48    pub fn resolve<T: std::ops::Deref<Target = str>>(
49        &mut self,
50        base: &Iri<T>,
51    ) -> Result<(), IriParseError> {
52        let mut out = String::with_capacity(self.as_str().len() + base.as_str().len());
53        let pos = resolve(
54            (base.as_str(), base.positions()),
55            (self.as_str(), self.positions),
56            &mut out,
57        );
58        validate_resolved_path(&out, pos)?;
59        *self = IriRefBuf::from_raw_parts(out, pos);
60        Ok(())
61    }
62
63    pub fn try_into_iri(self) -> Result<IriBuf, InvalidIri<IriRefBuf>> {
64        if self.is_absolute() {
65            let p = self.positions;
66            Ok(Iri::from_raw_parts(self.into_inner(), p))
67        } else {
68            Err(InvalidIri(self))
69        }
70    }
71
72    pub fn as_iri(&self) -> Option<Iri<&str>> {
73        if self.is_absolute() {
74            Some(Iri::from_raw_parts(self.as_str(), self.positions))
75        } else {
76            None
77        }
78    }
79}
80
81impl Default for IriRefBuf {
82    fn default() -> Self {
83        Self::parse_unchecked(String::new())
84    }
85}
86
87impl Default for UriRefBuf {
88    fn default() -> Self {
89        Self::parse_unchecked(String::new())
90    }
91}
92
93impl IriBuf {
94    pub fn set_scheme(&mut self, scheme: &str) -> Result<(), IriParseError> {
95        validate_scheme(scheme)?;
96        let p = self.positions();
97        let iri = std::mem::take(&mut self.0.iri);
98        let mut r = IriRefBuf::from_raw_parts(iri, p);
99        rebuild(&mut r, Edit::Scheme(Some(scheme.to_string())), true)?;
100        *self = Iri::from_raw_parts(r.iri, r.positions);
101        Ok(())
102    }
103
104    pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
105        let p = self.positions();
106        let iri = std::mem::take(&mut self.0.iri);
107        let mut r = IriRefBuf::from_raw_parts(iri, p);
108        r.set_authority(authority)?;
109        *self = Iri::from_raw_parts(r.iri, r.positions);
110        Ok(())
111    }
112
113    pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
114        let p = self.positions();
115        let iri = std::mem::take(&mut self.0.iri);
116        let mut r = IriRefBuf::from_raw_parts(iri, p);
117        r.set_path(path)?;
118        *self = Iri::from_raw_parts(r.iri, r.positions);
119        Ok(())
120    }
121
122    pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
123        let p = self.positions();
124        let iri = std::mem::take(&mut self.0.iri);
125        let mut r = IriRefBuf::from_raw_parts(iri, p);
126        r.set_query(query)?;
127        *self = Iri::from_raw_parts(r.iri, r.positions);
128        Ok(())
129    }
130
131    pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
132        let p = self.positions();
133        let iri = std::mem::take(&mut self.0.iri);
134        let mut r = IriRefBuf::from_raw_parts(iri, p);
135        r.set_fragment(fragment)?;
136        *self = Iri::from_raw_parts(r.iri, r.positions);
137        Ok(())
138    }
139}
140
141impl UriRefBuf {
142    pub fn set_scheme(&mut self, scheme: Option<&str>) -> Result<(), IriParseError> {
143        if let Some(s) = scheme {
144            validate_scheme(s)?;
145        }
146        rebuild_uri(self, Edit::Scheme(scheme.map(str::to_string)))
147    }
148    pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
149        if let Some(a) = authority {
150            validate_authority_body(a, false)?;
151        }
152        rebuild_uri(self, Edit::Authority(authority.map(str::to_string)))
153    }
154    pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
155        validate_path(path, false)?;
156        rebuild_uri(self, Edit::Path(path.to_string()))
157    }
158    pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
159        if let Some(q) = query {
160            validate_query(q, false)?;
161        }
162        rebuild_uri(self, Edit::Query(query.map(str::to_string)))
163    }
164    pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
165        if let Some(f) = fragment {
166            validate_fragment(f, false)?;
167        }
168        rebuild_uri(self, Edit::Fragment(fragment.map(str::to_string)))
169    }
170    pub fn resolve<T: std::ops::Deref<Target = str>>(
171        &mut self,
172        base: &Uri<T>,
173    ) -> Result<(), IriParseError> {
174        let mut out = String::with_capacity(self.as_str().len() + base.as_str().len());
175        let pos = resolve(
176            (base.as_str(), base.positions()),
177            (self.as_str(), self.positions),
178            &mut out,
179        );
180        validate_resolved_path(&out, pos)?;
181        *self = UriRefBuf::from_raw_parts(out, pos);
182        Ok(())
183    }
184
185    pub fn try_into_uri(self) -> Result<UriBuf, InvalidUri<UriRefBuf>> {
186        if self.is_absolute() {
187            let p = self.positions;
188            Ok(Uri::from_raw_parts(self.into_inner(), p))
189        } else {
190            Err(InvalidUri(self))
191        }
192    }
193
194    pub fn as_uri(&self) -> Option<Uri<&str>> {
195        if self.is_absolute() {
196            Some(Uri::from_raw_parts(self.as_str(), self.positions))
197        } else {
198            None
199        }
200    }
201}
202
203impl UriBuf {
204    pub fn set_scheme(&mut self, scheme: &str) -> Result<(), IriParseError> {
205        let p = self.positions();
206        let uri = std::mem::take(&mut self.0.uri);
207        let mut r = UriRefBuf::from_raw_parts(uri, p);
208        r.set_scheme(Some(scheme))?;
209        *self = Uri::from_raw_parts(r.uri, r.positions);
210        Ok(())
211    }
212    pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
213        let p = self.positions();
214        let uri = std::mem::take(&mut self.0.uri);
215        let mut r = UriRefBuf::from_raw_parts(uri, p);
216        r.set_authority(authority)?;
217        *self = Uri::from_raw_parts(r.uri, r.positions);
218        Ok(())
219    }
220    pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
221        let p = self.positions();
222        let uri = std::mem::take(&mut self.0.uri);
223        let mut r = UriRefBuf::from_raw_parts(uri, p);
224        r.set_path(path)?;
225        *self = Uri::from_raw_parts(r.uri, r.positions);
226        Ok(())
227    }
228    pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
229        let p = self.positions();
230        let uri = std::mem::take(&mut self.0.uri);
231        let mut r = UriRefBuf::from_raw_parts(uri, p);
232        r.set_query(query)?;
233        *self = Uri::from_raw_parts(r.uri, r.positions);
234        Ok(())
235    }
236    pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
237        let p = self.positions();
238        let uri = std::mem::take(&mut self.0.uri);
239        let mut r = UriRefBuf::from_raw_parts(uri, p);
240        r.set_fragment(fragment)?;
241        *self = Uri::from_raw_parts(r.uri, r.positions);
242        Ok(())
243    }
244}
245
246enum Edit {
247    Scheme(Option<String>),
248    Authority(Option<String>),
249    Path(String),
250    Query(Option<String>),
251    Fragment(Option<String>),
252}
253
254fn rebuild(buf: &mut IriRefBuf, edit: Edit, is_iri: bool) -> Result<(), IriParseError> {
255    let out = {
256        let s = buf.as_str();
257        let (scheme, authority, path, query, fragment) = destructure(s, buf.positions);
258        let mut out = String::with_capacity(s.len() + 8);
259        match &edit {
260            Edit::Scheme(new) => {
261                write_iri(&mut out, new.as_deref(), authority, path, query, fragment)
262            }
263            Edit::Authority(new) => {
264                write_iri(&mut out, scheme, new.as_deref(), path, query, fragment)
265            }
266            Edit::Path(new) => write_iri(&mut out, scheme, authority, new, query, fragment),
267            Edit::Query(new) => {
268                write_iri(&mut out, scheme, authority, path, new.as_deref(), fragment)
269            }
270            Edit::Fragment(new) => {
271                write_iri(&mut out, scheme, authority, path, query, new.as_deref())
272            }
273        }
274        out
275    };
276    let positions = find_iri_ref_positions(&out);
277    validate_iri_ref(&out, positions, is_iri)?;
278    *buf = IriRef::from_raw_parts(out, positions);
279    Ok(())
280}
281
282fn write_iri(
283    out: &mut String,
284    scheme: Option<&str>,
285    authority: Option<&str>,
286    path: &str,
287    query: Option<&str>,
288    fragment: Option<&str>,
289) {
290    if let Some(s) = scheme {
291        out.push_str(s);
292        out.push(':');
293    }
294    if let Some(a) = authority {
295        out.push_str("//");
296        out.push_str(a);
297    }
298    out.push_str(path);
299    if let Some(q) = query {
300        out.push('?');
301        out.push_str(q);
302    }
303    if let Some(f) = fragment {
304        out.push('#');
305        out.push_str(f);
306    }
307}
308
309fn rebuild_uri(buf: &mut UriRefBuf, edit: Edit) -> Result<(), IriParseError> {
310    let mut tmp: IriRefBuf = IriRef::from_raw_parts(buf.as_str().to_string(), buf.positions);
311    rebuild(&mut tmp, edit, false)?;
312    let p = tmp.positions;
313    *buf = UriRef::from_raw_parts(tmp.iri, p);
314    Ok(())
315}
316
317fn destructure(
318    s: &str,
319    p: crate::parse::Positions,
320) -> (Option<&str>, Option<&str>, &str, Option<&str>, Option<&str>) {
321    let scheme = if p.scheme_end > 0 {
322        Some(&s[..p.scheme_end - 1])
323    } else {
324        None
325    };
326    let authority = if p.authority_end > p.scheme_end + 2
327        || (p.authority_end == p.scheme_end + 2
328            && p.scheme_end + 2 <= s.len()
329            && &s[p.scheme_end..p.scheme_end + 2] == "//")
330    {
331        Some(&s[p.scheme_end + 2..p.authority_end])
332    } else {
333        None
334    };
335    let path = &s[p.authority_end..p.path_end];
336    let query = if p.query_end > p.path_end {
337        Some(&s[p.path_end + 1..p.query_end])
338    } else {
339        None
340    };
341    let fragment = if s.len() > p.query_end {
342        Some(&s[p.query_end + 1..])
343    } else {
344        None
345    };
346    (scheme, authority, path, query, fragment)
347}
348