Coverage Report

Created: 2026-03-17 06:50

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