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 std::ops::{Range, RangeFrom, RangeTo, RangeFull, Index}; 10 use Url; 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 => if self.has_authority() { 109 self.scheme_end as usize + "://".len() 110 } else { 111 debug_assert!(self.byte_at(self.scheme_end) == b':'); 112 debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end); 113 self.scheme_end as usize + ":".len() 114 }, 115 116 Position::AfterUsername => self.username_end as usize, 117 118 Position::BeforePassword => if self.has_authority() && 119 self.byte_at(self.username_end) == b':' { 120 self.username_end as usize + ":".len() 121 } else { 122 debug_assert!(self.username_end == self.host_start); 123 self.username_end as usize 124 }, 125 126 Position::AfterPassword => if self.has_authority() && 127 self.byte_at(self.username_end) == b':' { 128 debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@'); 129 self.host_start as usize - "@".len() 130 } else { 131 debug_assert!(self.username_end == self.host_start); 132 self.host_start as usize 133 }, 134 135 Position::BeforeHost => self.host_start as usize, 136 137 Position::AfterHost => self.host_end as usize, 138 139 Position::BeforePort => if self.port.is_some() { 140 debug_assert!(self.byte_at(self.host_end) == b':'); 141 self.host_end as usize + ":".len() 142 } else { 143 self.host_end as usize 144 }, 145 146 Position::AfterPort => self.path_start as usize, 147 148 Position::BeforePath => self.path_start as usize, 149 150 Position::AfterPath => match (self.query_start, self.fragment_start) { 151 (Some(q), _) => q as usize, 152 (None, Some(f)) => f as usize, 153 (None, None) => self.serialization.len(), 154 }, 155 156 Position::BeforeQuery => match (self.query_start, self.fragment_start) { 157 (Some(q), _) => { 158 debug_assert!(self.byte_at(q) == b'?'); 159 q as usize + "?".len() 160 } 161 (None, Some(f)) => f as usize, 162 (None, None) => self.serialization.len(), 163 }, 164 165 Position::AfterQuery => match self.fragment_start { 166 None => self.serialization.len(), 167 Some(f) => f as usize, 168 }, 169 170 Position::BeforeFragment => match self.fragment_start { 171 Some(f) => { 172 debug_assert!(self.byte_at(f) == b'#'); 173 f as usize + "#".len() 174 } 175 None => self.serialization.len(), 176 }, 177 178 Position::AfterFragment => self.serialization.len(), 179 } 180 } 181 } 182 183