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 /// Indicates a position within a URL based on its components. 41 /// 42 /// A range of positions can be used for slicing `Url`: 43 /// 44 /// ```rust 45 /// # use url::{Url, Position}; 46 /// # fn something(some_url: Url) { 47 /// let serialization: &str = &some_url[..]; 48 /// let serialization_without_fragment: &str = &some_url[..Position::AfterQuery]; 49 /// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort]; 50 /// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery]; 51 /// let scheme_relative: &str = &some_url[Position::BeforeUsername..]; 52 /// # } 53 /// ``` 54 /// 55 /// In a pseudo-grammar (where `[`…`]?` makes a sub-sequence optional), 56 /// URL components and delimiters that separate them are: 57 /// 58 /// ```notrust 59 /// url = 60 /// scheme ":" 61 /// [ "//" [ username [ ":" password ]? "@" ]? host [ ":" port ]? ]? 62 /// path [ "?" query ]? [ "#" fragment ]? 63 /// ``` 64 /// 65 /// When a given component is not present, 66 /// its "before" and "after" position are the same 67 /// (so that `&some_url[BeforeFoo..AfterFoo]` is the empty string) 68 /// and component ordering is preserved 69 /// (so that a missing query "is between" a path and a fragment). 70 /// 71 /// The end of a component and the start of the next are either the same or separate 72 /// by a delimiter. 73 /// (Not that the initial `/` of a path is considered part of the path here, not a delimiter.) 74 /// For example, `&url[..BeforeFragment]` would include a `#` delimiter (if present in `url`), 75 /// so `&url[..AfterQuery]` might be desired instead. 76 /// 77 /// `BeforeScheme` and `AfterFragment` are always the start and end of the entire URL, 78 /// so `&url[BeforeScheme..X]` is the same as `&url[..X]` 79 /// and `&url[X..AfterFragment]` is the same as `&url[X..]`. 80 #[derive(Copy, Clone, Debug)] 81 pub enum Position { 82 BeforeScheme, 83 AfterScheme, 84 BeforeUsername, 85 AfterUsername, 86 BeforePassword, 87 AfterPassword, 88 BeforeHost, 89 AfterHost, 90 BeforePort, 91 AfterPort, 92 BeforePath, 93 AfterPath, 94 BeforeQuery, 95 AfterQuery, 96 BeforeFragment, 97 AfterFragment, 98 } 99 100 impl Url { 101 #[inline] index(&self, position: Position) -> usize102 fn index(&self, position: Position) -> usize { 103 match position { 104 Position::BeforeScheme => 0, 105 106 Position::AfterScheme => self.scheme_end as usize, 107 108 Position::BeforeUsername => { 109 if self.has_authority() { 110 self.scheme_end as usize + "://".len() 111 } else { 112 debug_assert!(self.byte_at(self.scheme_end) == b':'); 113 debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end); 114 self.scheme_end as usize + ":".len() 115 } 116 } 117 118 Position::AfterUsername => self.username_end as usize, 119 120 Position::BeforePassword => { 121 if self.has_authority() && self.byte_at(self.username_end) == b':' { 122 self.username_end as usize + ":".len() 123 } else { 124 debug_assert!(self.username_end == self.host_start); 125 self.username_end as usize 126 } 127 } 128 129 Position::AfterPassword => { 130 if self.has_authority() && self.byte_at(self.username_end) == b':' { 131 debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@'); 132 self.host_start as usize - "@".len() 133 } else { 134 debug_assert!(self.username_end == self.host_start); 135 self.host_start as usize 136 } 137 } 138 139 Position::BeforeHost => self.host_start as usize, 140 141 Position::AfterHost => self.host_end as usize, 142 143 Position::BeforePort => { 144 if self.port.is_some() { 145 debug_assert!(self.byte_at(self.host_end) == b':'); 146 self.host_end as usize + ":".len() 147 } else { 148 self.host_end as usize 149 } 150 } 151 152 Position::AfterPort => self.path_start as usize, 153 154 Position::BeforePath => self.path_start as usize, 155 156 Position::AfterPath => match (self.query_start, self.fragment_start) { 157 (Some(q), _) => q as usize, 158 (None, Some(f)) => f as usize, 159 (None, None) => self.serialization.len(), 160 }, 161 162 Position::BeforeQuery => match (self.query_start, self.fragment_start) { 163 (Some(q), _) => { 164 debug_assert!(self.byte_at(q) == b'?'); 165 q as usize + "?".len() 166 } 167 (None, Some(f)) => f as usize, 168 (None, None) => self.serialization.len(), 169 }, 170 171 Position::AfterQuery => match self.fragment_start { 172 None => self.serialization.len(), 173 Some(f) => f as usize, 174 }, 175 176 Position::BeforeFragment => match self.fragment_start { 177 Some(f) => { 178 debug_assert!(self.byte_at(f) == b'#'); 179 f as usize + "#".len() 180 } 181 None => self.serialization.len(), 182 }, 183 184 Position::AfterFragment => self.serialization.len(), 185 } 186 } 187 } 188