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