// Copyright 2016 The rust-url developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo}; use Url; impl Index for Url { type Output = str; fn index(&self, _: RangeFull) -> &str { &self.serialization } } impl Index> for Url { type Output = str; fn index(&self, range: RangeFrom) -> &str { &self.serialization[self.index(range.start)..] } } impl Index> for Url { type Output = str; fn index(&self, range: RangeTo) -> &str { &self.serialization[..self.index(range.end)] } } impl Index> for Url { type Output = str; fn index(&self, range: Range) -> &str { &self.serialization[self.index(range.start)..self.index(range.end)] } } /// Indicates a position within a URL based on its components. /// /// A range of positions can be used for slicing `Url`: /// /// ```rust /// # use url::{Url, Position}; /// # fn something(some_url: Url) { /// let serialization: &str = &some_url[..]; /// let serialization_without_fragment: &str = &some_url[..Position::AfterQuery]; /// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort]; /// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery]; /// let scheme_relative: &str = &some_url[Position::BeforeUsername..]; /// # } /// ``` /// /// In a pseudo-grammar (where `[`…`]?` makes a sub-sequence optional), /// URL components and delimiters that separate them are: /// /// ```notrust /// url = /// scheme ":" /// [ "//" [ username [ ":" password ]? "@" ]? host [ ":" port ]? ]? /// path [ "?" query ]? [ "#" fragment ]? /// ``` /// /// When a given component is not present, /// its "before" and "after" position are the same /// (so that `&some_url[BeforeFoo..AfterFoo]` is the empty string) /// and component ordering is preserved /// (so that a missing query "is between" a path and a fragment). /// /// The end of a component and the start of the next are either the same or separate /// by a delimiter. /// (Not that the initial `/` of a path is considered part of the path here, not a delimiter.) /// For example, `&url[..BeforeFragment]` would include a `#` delimiter (if present in `url`), /// so `&url[..AfterQuery]` might be desired instead. /// /// `BeforeScheme` and `AfterFragment` are always the start and end of the entire URL, /// so `&url[BeforeScheme..X]` is the same as `&url[..X]` /// and `&url[X..AfterFragment]` is the same as `&url[X..]`. #[derive(Copy, Clone, Debug)] pub enum Position { BeforeScheme, AfterScheme, BeforeUsername, AfterUsername, BeforePassword, AfterPassword, BeforeHost, AfterHost, BeforePort, AfterPort, BeforePath, AfterPath, BeforeQuery, AfterQuery, BeforeFragment, AfterFragment, } impl Url { #[inline] fn index(&self, position: Position) -> usize { match position { Position::BeforeScheme => 0, Position::AfterScheme => self.scheme_end as usize, Position::BeforeUsername => { if self.has_authority() { self.scheme_end as usize + "://".len() } else { debug_assert!(self.byte_at(self.scheme_end) == b':'); debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end); self.scheme_end as usize + ":".len() } } Position::AfterUsername => self.username_end as usize, Position::BeforePassword => { if self.has_authority() && self.byte_at(self.username_end) == b':' { self.username_end as usize + ":".len() } else { debug_assert!(self.username_end == self.host_start); self.username_end as usize } } Position::AfterPassword => { if self.has_authority() && self.byte_at(self.username_end) == b':' { debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@'); self.host_start as usize - "@".len() } else { debug_assert!(self.username_end == self.host_start); self.host_start as usize } } Position::BeforeHost => self.host_start as usize, Position::AfterHost => self.host_end as usize, Position::BeforePort => { if self.port.is_some() { debug_assert!(self.byte_at(self.host_end) == b':'); self.host_end as usize + ":".len() } else { self.host_end as usize } } Position::AfterPort => self.path_start as usize, Position::BeforePath => self.path_start as usize, Position::AfterPath => match (self.query_start, self.fragment_start) { (Some(q), _) => q as usize, (None, Some(f)) => f as usize, (None, None) => self.serialization.len(), }, Position::BeforeQuery => match (self.query_start, self.fragment_start) { (Some(q), _) => { debug_assert!(self.byte_at(q) == b'?'); q as usize + "?".len() } (None, Some(f)) => f as usize, (None, None) => self.serialization.len(), }, Position::AfterQuery => match self.fragment_start { None => self.serialization.len(), Some(f) => f as usize, }, Position::BeforeFragment => match self.fragment_start { Some(f) => { debug_assert!(self.byte_at(f) == b'#'); f as usize + "#".len() } None => self.serialization.len(), }, Position::AfterFragment => self.serialization.len(), } } }