• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 crate::Url;
10 use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
11 
12 impl Index<RangeFull> for Url {
13     type Output = str;
index(&self, _: RangeFull) -> &str14     fn index(&self, _: RangeFull) -> &str {
15         &self.serialization
16     }
17 }
18 
19 impl Index<RangeFrom<Position>> for Url {
20     type Output = str;
index(&self, range: RangeFrom<Position>) -> &str21     fn index(&self, range: RangeFrom<Position>) -> &str {
22         &self.serialization[self.index(range.start)..]
23     }
24 }
25 
26 impl Index<RangeTo<Position>> for Url {
27     type Output = str;
index(&self, range: RangeTo<Position>) -> &str28     fn index(&self, range: RangeTo<Position>) -> &str {
29         &self.serialization[..self.index(range.end)]
30     }
31 }
32 
33 impl Index<Range<Position>> for Url {
34     type Output = str;
index(&self, range: Range<Position>) -> &str35     fn index(&self, range: Range<Position>) -> &str {
36         &self.serialization[self.index(range.start)..self.index(range.end)]
37     }
38 }
39 
40 // Counts how many base-10 digits are required to represent n in the given base
count_digits(n: u16) -> usize41 fn 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]
test_count_digits()52 fn 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)]
104 pub 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 
123 impl Url {
124     #[inline]
index(&self, position: Position) -> usize125     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