Coverage Report

Created: 2025-10-10 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/url-2.5.4/src/slicing.rs
Line
Count
Source
1
// Copyright 2016 The rust-url developers.
2
//
3
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6
// option. This file may not be copied, modified, or distributed
7
// except according to those terms.
8
9
use core::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
10
11
use crate::Url;
12
13
impl Index<RangeFull> for Url {
14
    type Output = str;
15
0
    fn index(&self, _: RangeFull) -> &str {
16
0
        &self.serialization
17
0
    }
18
}
19
20
impl Index<RangeFrom<Position>> for Url {
21
    type Output = str;
22
0
    fn index(&self, range: RangeFrom<Position>) -> &str {
23
0
        &self.serialization[self.index(range.start)..]
24
0
    }
25
}
26
27
impl Index<RangeTo<Position>> for Url {
28
    type Output = str;
29
0
    fn index(&self, range: RangeTo<Position>) -> &str {
30
0
        &self.serialization[..self.index(range.end)]
31
0
    }
32
}
33
34
impl Index<Range<Position>> for Url {
35
    type Output = str;
36
0
    fn index(&self, range: Range<Position>) -> &str {
37
0
        &self.serialization[self.index(range.start)..self.index(range.end)]
38
0
    }
39
}
40
41
// Counts how many base-10 digits are required to represent n in the given base
42
0
fn count_digits(n: u16) -> usize {
43
0
    match n {
44
0
        0..=9 => 1,
45
0
        10..=99 => 2,
46
0
        100..=999 => 3,
47
0
        1000..=9999 => 4,
48
0
        10000..=65535 => 5,
49
    }
50
0
}
51
52
#[test]
53
fn test_count_digits() {
54
    assert_eq!(count_digits(0), 1);
55
    assert_eq!(count_digits(1), 1);
56
    assert_eq!(count_digits(9), 1);
57
    assert_eq!(count_digits(10), 2);
58
    assert_eq!(count_digits(99), 2);
59
    assert_eq!(count_digits(100), 3);
60
    assert_eq!(count_digits(9999), 4);
61
    assert_eq!(count_digits(65535), 5);
62
}
63
64
/// Indicates a position within a URL based on its components.
65
///
66
/// A range of positions can be used for slicing `Url`:
67
///
68
/// ```rust
69
/// # use url::{Url, Position};
70
/// # fn something(some_url: Url) {
71
/// let serialization: &str = &some_url[..];
72
/// let serialization_without_fragment: &str = &some_url[..Position::AfterQuery];
73
/// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort];
74
/// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery];
75
/// let scheme_relative: &str = &some_url[Position::BeforeUsername..];
76
/// # }
77
/// ```
78
///
79
/// In a pseudo-grammar (where `[`…`]?` makes a sub-sequence optional),
80
/// URL components and delimiters that separate them are:
81
///
82
/// ```notrust
83
/// url =
84
///     scheme ":"
85
///     [ "//" [ username [ ":" password ]? "@" ]? host [ ":" port ]? ]?
86
///     path [ "?" query ]? [ "#" fragment ]?
87
/// ```
88
///
89
/// When a given component is not present,
90
/// its "before" and "after" position are the same
91
/// (so that `&some_url[BeforeFoo..AfterFoo]` is the empty string)
92
/// and component ordering is preserved
93
/// (so that a missing query "is between" a path and a fragment).
94
///
95
/// The end of a component and the start of the next are either the same or separate
96
/// by a delimiter.
97
/// (Note that the initial `/` of a path is considered part of the path here, not a delimiter.)
98
/// For example, `&url[..BeforeFragment]` would include a `#` delimiter (if present in `url`),
99
/// so `&url[..AfterQuery]` might be desired instead.
100
///
101
/// `BeforeScheme` and `AfterFragment` are always the start and end of the entire URL,
102
/// so `&url[BeforeScheme..X]` is the same as `&url[..X]`
103
/// and `&url[X..AfterFragment]` is the same as `&url[X..]`.
104
#[derive(Copy, Clone, Debug)]
105
pub enum Position {
106
    BeforeScheme,
107
    AfterScheme,
108
    BeforeUsername,
109
    AfterUsername,
110
    BeforePassword,
111
    AfterPassword,
112
    BeforeHost,
113
    AfterHost,
114
    BeforePort,
115
    AfterPort,
116
    BeforePath,
117
    AfterPath,
118
    BeforeQuery,
119
    AfterQuery,
120
    BeforeFragment,
121
    AfterFragment,
122
}
123
124
impl Url {
125
    #[inline]
126
0
    fn index(&self, position: Position) -> usize {
127
0
        match position {
128
0
            Position::BeforeScheme => 0,
129
130
0
            Position::AfterScheme => self.scheme_end as usize,
131
132
            Position::BeforeUsername => {
133
0
                if self.has_authority() {
134
0
                    self.scheme_end as usize + "://".len()
135
                } else {
136
0
                    debug_assert!(self.byte_at(self.scheme_end) == b':');
137
0
                    debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
138
0
                    self.scheme_end as usize + ":".len()
139
                }
140
            }
141
142
0
            Position::AfterUsername => self.username_end as usize,
143
144
            Position::BeforePassword => {
145
0
                if self.has_authority() && self.byte_at(self.username_end) == b':' {
146
0
                    self.username_end as usize + ":".len()
147
                } else {
148
0
                    debug_assert!(self.username_end == self.host_start);
149
0
                    self.username_end as usize
150
                }
151
            }
152
153
            Position::AfterPassword => {
154
0
                if self.has_authority() && self.byte_at(self.username_end) == b':' {
155
0
                    debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
156
0
                    self.host_start as usize - "@".len()
157
                } else {
158
0
                    debug_assert!(self.username_end == self.host_start);
159
0
                    self.host_start as usize
160
                }
161
            }
162
163
0
            Position::BeforeHost => self.host_start as usize,
164
165
0
            Position::AfterHost => self.host_end as usize,
166
167
            Position::BeforePort => {
168
0
                if self.port.is_some() {
169
0
                    debug_assert!(self.byte_at(self.host_end) == b':');
170
0
                    self.host_end as usize + ":".len()
171
                } else {
172
0
                    self.host_end as usize
173
                }
174
            }
175
176
            Position::AfterPort => {
177
0
                if let Some(port) = self.port {
178
0
                    debug_assert!(self.byte_at(self.host_end) == b':');
179
0
                    self.host_end as usize + ":".len() + count_digits(port)
180
                } else {
181
0
                    self.host_end as usize
182
                }
183
            }
184
185
0
            Position::BeforePath => self.path_start as usize,
186
187
0
            Position::AfterPath => match (self.query_start, self.fragment_start) {
188
0
                (Some(q), _) => q as usize,
189
0
                (None, Some(f)) => f as usize,
190
0
                (None, None) => self.serialization.len(),
191
            },
192
193
0
            Position::BeforeQuery => match (self.query_start, self.fragment_start) {
194
0
                (Some(q), _) => {
195
0
                    debug_assert!(self.byte_at(q) == b'?');
196
0
                    q as usize + "?".len()
197
                }
198
0
                (None, Some(f)) => f as usize,
199
0
                (None, None) => self.serialization.len(),
200
            },
201
202
0
            Position::AfterQuery => match self.fragment_start {
203
0
                None => self.serialization.len(),
204
0
                Some(f) => f as usize,
205
            },
206
207
0
            Position::BeforeFragment => match self.fragment_start {
208
0
                Some(f) => {
209
0
                    debug_assert!(self.byte_at(f) == b'#');
210
0
                    f as usize + "#".len()
211
                }
212
0
                None => self.serialization.len(),
213
            },
214
215
0
            Position::AfterFragment => self.serialization.len(),
216
        }
217
0
    }
218
}