iri_string/
components.rs

1//! Components of IRIs.
2
3mod authority;
4
5use core::num::NonZeroUsize;
6use core::ops::{Range, RangeFrom, RangeTo};
7
8use crate::parser::trusted as trusted_parser;
9use crate::spec::Spec;
10use crate::types::RiReferenceStr;
11
12pub use self::authority::AuthorityComponents;
13
14/// Positions to split an IRI into components.
15#[derive(Debug, Clone, Copy)]
16pub(crate) struct Splitter {
17    /// Scheme end.
18    scheme_end: Option<NonZeroUsize>,
19    /// Authority end.
20    ///
21    /// Note that absence of the authority and the empty authority is
22    /// distinguished.
23    authority_end: Option<NonZeroUsize>,
24    /// Query start (after the leading `?`).
25    query_start: Option<NonZeroUsize>,
26    /// Fragment start (after the leading `#`).
27    fragment_start: Option<NonZeroUsize>,
28}
29
30impl Splitter {
31    /// Creates a new splitter.
32    #[inline]
33    #[must_use]
34    pub(crate) fn new(
35        scheme_end: Option<NonZeroUsize>,
36        authority_end: Option<NonZeroUsize>,
37        query_start: Option<NonZeroUsize>,
38        fragment_start: Option<NonZeroUsize>,
39    ) -> Self {
40        Self {
41            scheme_end,
42            authority_end,
43            query_start,
44            fragment_start,
45        }
46    }
47
48    /// Decomposes an IRI into five major components: scheme, authority, path, query, and fragment.
49    #[must_use]
50    fn split_into_major(
51        self,
52        s: &str,
53    ) -> (Option<&str>, Option<&str>, &str, Option<&str>, Option<&str>) {
54        let (scheme, next_of_scheme) = match self.scheme_end {
55            // +1: ":".len()
56            Some(end) => (Some(&s[..end.get()]), end.get() + 1),
57            None => (None, 0),
58        };
59        let (authority, next_of_authority) = match self.authority_end {
60            // +2: "//".len()
61            Some(end) => (Some(&s[(next_of_scheme + 2)..end.get()]), end.get()),
62            None => (None, next_of_scheme),
63        };
64        let (fragment, end_of_prev_of_fragment) = match self.fragment_start {
65            // -1: "#".len()
66            Some(start) => (Some(&s[start.get()..]), start.get() - 1),
67            None => (None, s.len()),
68        };
69        let (query, end_of_path) = match self.query_start {
70            Some(start) => (
71                Some(&s[start.get()..end_of_prev_of_fragment]),
72                // -1: "?".len()
73                start.get() - 1,
74            ),
75            None => (None, end_of_prev_of_fragment),
76        };
77        let path = &s[next_of_authority..end_of_path];
78        (scheme, authority, path, query, fragment)
79    }
80
81    /// Returns the range for the scheme part.
82    #[inline]
83    #[must_use]
84    fn scheme_range(self) -> Option<RangeTo<usize>> {
85        self.scheme_end.map(|end| ..end.get())
86    }
87
88    /// Returns the scheme as a string.
89    #[inline]
90    #[must_use]
91    pub(crate) fn scheme_str<'a>(&self, s: &'a str) -> Option<&'a str> {
92        self.scheme_range().map(|range| &s[range])
93    }
94
95    /// Returns true if the IRI has a scheme part, false otherwise.
96    #[inline]
97    #[must_use]
98    pub(crate) fn has_scheme(&self) -> bool {
99        self.scheme_end.is_some()
100    }
101
102    /// Returns the range for the authority part.
103    #[inline]
104    #[must_use]
105    fn authority_range(self) -> Option<Range<usize>> {
106        let end = self.authority_end?.get();
107        // 2: "//".len()
108        // +3: "://".len()
109        let start = self.scheme_end.map_or(2, |v| v.get() + 3);
110        Some(start..end)
111    }
112
113    /// Returns the authority as a string.
114    #[inline]
115    #[must_use]
116    pub(crate) fn authority_str<'a>(&self, s: &'a str) -> Option<&'a str> {
117        self.authority_range().map(|range| &s[range])
118    }
119
120    /// Returns true if the IRI has an authority part, false otherwise.
121    #[inline]
122    #[must_use]
123    pub(crate) fn has_authority(&self) -> bool {
124        self.authority_end.is_some()
125    }
126
127    /// Returns the range for the path part.
128    #[inline]
129    #[must_use]
130    fn path_range(self, full_len: usize) -> Range<usize> {
131        // -1: "?".len() and "#".len()
132        let end = self
133            .query_start
134            .or(self.fragment_start)
135            .map_or(full_len, |v| v.get() - 1);
136        let start = self.authority_end.map_or_else(
137            // +1: ":".len()
138            || self.scheme_end.map_or(0, |v| v.get() + 1),
139            NonZeroUsize::get,
140        );
141
142        start..end
143    }
144
145    /// Returns the path as a string.
146    #[inline]
147    #[must_use]
148    pub(crate) fn path_str<'a>(&self, s: &'a str) -> &'a str {
149        &s[self.path_range(s.len())]
150    }
151
152    /// Returns true if the path part of the IRI is empty.
153    #[inline]
154    #[must_use]
155    pub(crate) fn is_path_empty(&self, full_len: usize) -> bool {
156        self.path_range(full_len).is_empty()
157    }
158
159    /// Returns the range for the query part excluding a prefix `?`.
160    #[inline]
161    #[must_use]
162    fn query_range(self, full_len: usize) -> Option<Range<usize>> {
163        let start = self.query_start?.get();
164        // -1: "#".len()
165        let end = self.fragment_start.map_or(full_len, |v| v.get() - 1);
166
167        Some(start..end)
168    }
169
170    /// Returns the query as a string.
171    #[inline]
172    #[must_use]
173    pub(crate) fn query_str<'a>(&self, s: &'a str) -> Option<&'a str> {
174        self.query_range(s.len()).map(|range| &s[range])
175    }
176
177    /// Returns true if the IRI has a query part, false otherwise.
178    #[inline]
179    #[must_use]
180    pub(crate) fn has_query(&self) -> bool {
181        self.query_start.is_some()
182    }
183
184    /// Returns the range for the fragment part excluding a prefix `#`.
185    #[inline]
186    #[must_use]
187    pub(crate) fn fragment_range(self) -> Option<RangeFrom<usize>> {
188        self.fragment_start.map(|v| v.get()..)
189    }
190
191    /// Returns the fragment as a string.
192    #[inline]
193    #[must_use]
194    pub(crate) fn fragment_str<'a>(&self, s: &'a str) -> Option<&'a str> {
195        self.fragment_range().map(|range| &s[range])
196    }
197}
198
199/// Components of an IRI reference.
200///
201/// See <https://tools.ietf.org/html/rfc3986#section-5.2.2>.
202#[derive(Debug, Clone, Copy)]
203pub(crate) struct RiReferenceComponents<'a, S: Spec> {
204    /// Original complete string.
205    pub(crate) iri: &'a RiReferenceStr<S>,
206    /// Positions to split the IRI into components.
207    pub(crate) splitter: Splitter,
208}
209
210impl<'a, S: Spec> RiReferenceComponents<'a, S> {
211    /// Returns five major components: scheme, authority, path, query, and fragment.
212    #[inline]
213    #[must_use]
214    pub(crate) fn to_major(
215        self,
216    ) -> (
217        Option<&'a str>,
218        Option<&'a str>,
219        &'a str,
220        Option<&'a str>,
221        Option<&'a str>,
222    ) {
223        self.splitter.split_into_major(self.iri.as_str())
224    }
225
226    /// Returns the IRI reference.
227    #[inline]
228    #[must_use]
229    pub(crate) fn iri(&self) -> &'a RiReferenceStr<S> {
230        self.iri
231    }
232
233    /// Returns the scheme as a string.
234    #[inline]
235    #[must_use]
236    pub(crate) fn scheme_str(&self) -> Option<&str> {
237        self.splitter.scheme_str(self.iri.as_str())
238    }
239
240    /// Returns the authority as a string.
241    #[inline]
242    #[must_use]
243    pub(crate) fn authority_str(&self) -> Option<&str> {
244        self.splitter.authority_str(self.iri.as_str())
245    }
246
247    /// Returns the path as a string.
248    #[inline]
249    #[must_use]
250    pub(crate) fn path_str(&self) -> &str {
251        self.splitter.path_str(self.iri.as_str())
252    }
253
254    /// Returns the query as a string.
255    #[inline]
256    #[must_use]
257    pub(crate) fn query_str(&self) -> Option<&str> {
258        self.splitter.query_str(self.iri.as_str())
259    }
260}
261
262impl<'a, S: Spec> From<&'a RiReferenceStr<S>> for RiReferenceComponents<'a, S> {
263    #[inline]
264    fn from(s: &'a RiReferenceStr<S>) -> Self {
265        trusted_parser::decompose_iri_reference(s)
266    }
267}