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
9use core::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
10
11use crate::Url;
12
13impl Index<RangeFull> for Url {
14 type Output = str;
15 fn index(&self, _: RangeFull) -> &str {
16 &self.serialization
17 }
18}
19
20impl Index<RangeFrom<Position>> for Url {
21 type Output = str;
22 fn index(&self, range: RangeFrom<Position>) -> &str {
23 &self.serialization[self.index(position:range.start)..]
24 }
25}
26
27impl Index<RangeTo<Position>> for Url {
28 type Output = str;
29 fn index(&self, range: RangeTo<Position>) -> &str {
30 &self.serialization[..self.index(position:range.end)]
31 }
32}
33
34impl Index<Range<Position>> for Url {
35 type Output = str;
36 fn index(&self, range: Range<Position>) -> &str {
37 &self.serialization[self.index(position:range.start)..self.index(position:range.end)]
38 }
39}
40
41// Counts how many base-10 digits are required to represent n in the given base
42fn count_digits(n: u16) -> usize {
43 match n {
44 0..=9 => 1,
45 10..=99 => 2,
46 100..=999 => 3,
47 1000..=9999 => 4,
48 10000..=65535 => 5,
49 }
50}
51
52#[test]
53fn 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)]
105pub 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
124impl Url {
125 #[inline]
126 fn index(&self, position: Position) -> usize {
127 match position {
128 Position::BeforeScheme => 0,
129
130 Position::AfterScheme => self.scheme_end as usize,
131
132 Position::BeforeUsername => {
133 if self.has_authority() {
134 self.scheme_end as usize + "://".len()
135 } else {
136 debug_assert!(self.byte_at(self.scheme_end) == b':');
137 debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
138 self.scheme_end as usize + ":".len()
139 }
140 }
141
142 Position::AfterUsername => self.username_end as usize,
143
144 Position::BeforePassword => {
145 if self.has_authority() && self.byte_at(self.username_end) == b':' {
146 self.username_end as usize + ":".len()
147 } else {
148 debug_assert!(self.username_end == self.host_start);
149 self.username_end as usize
150 }
151 }
152
153 Position::AfterPassword => {
154 if self.has_authority() && self.byte_at(self.username_end) == b':' {
155 debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
156 self.host_start as usize - "@".len()
157 } else {
158 debug_assert!(self.username_end == self.host_start);
159 self.host_start as usize
160 }
161 }
162
163 Position::BeforeHost => self.host_start as usize,
164
165 Position::AfterHost => self.host_end as usize,
166
167 Position::BeforePort => {
168 if self.port.is_some() {
169 debug_assert!(self.byte_at(self.host_end) == b':');
170 self.host_end as usize + ":".len()
171 } else {
172 self.host_end as usize
173 }
174 }
175
176 Position::AfterPort => {
177 if let Some(port) = self.port {
178 debug_assert!(self.byte_at(self.host_end) == b':');
179 self.host_end as usize + ":".len() + count_digits(port)
180 } else {
181 self.host_end as usize
182 }
183 }
184
185 Position::BeforePath => self.path_start as usize,
186
187 Position::AfterPath => match (self.query_start, self.fragment_start) {
188 (Some(q), _) => q as usize,
189 (None, Some(f)) => f as usize,
190 (None, None) => self.serialization.len(),
191 },
192
193 Position::BeforeQuery => match (self.query_start, self.fragment_start) {
194 (Some(q), _) => {
195 debug_assert!(self.byte_at(q) == b'?');
196 q as usize + "?".len()
197 }
198 (None, Some(f)) => f as usize,
199 (None, None) => self.serialization.len(),
200 },
201
202 Position::AfterQuery => match self.fragment_start {
203 None => self.serialization.len(),
204 Some(f) => f as usize,
205 },
206
207 Position::BeforeFragment => match self.fragment_start {
208 Some(f) => {
209 debug_assert!(self.byte_at(f) == b'#');
210 f as usize + "#".len()
211 }
212 None => self.serialization.len(),
213 },
214
215 Position::AfterFragment => self.serialization.len(),
216 }
217 }
218}
219