//! Components of IRIs. mod authority; use core::num::NonZeroUsize; use core::ops::{Range, RangeFrom, RangeTo}; use crate::parser::trusted as trusted_parser; use crate::spec::Spec; use crate::types::RiReferenceStr; pub use self::authority::AuthorityComponents; /// Positions to split an IRI into components. #[derive(Debug, Clone, Copy)] pub(crate) struct Splitter { /// Scheme end. scheme_end: Option, /// Authority end. /// /// Note that absence of the authority and the empty authority is /// distinguished. authority_end: Option, /// Query start (after the leading `?`). query_start: Option, /// Fragment start (after the leading `#`). fragment_start: Option, } impl Splitter { /// Creates a new splitter. #[inline] #[must_use] pub(crate) fn new( scheme_end: Option, authority_end: Option, query_start: Option, fragment_start: Option, ) -> Self { Self { scheme_end, authority_end, query_start, fragment_start, } } /// Decomposes an IRI into five major components: scheme, authority, path, query, and fragment. #[must_use] fn split_into_major( self, s: &str, ) -> (Option<&str>, Option<&str>, &str, Option<&str>, Option<&str>) { let (scheme, next_of_scheme) = match self.scheme_end { // +1: ":".len() Some(end) => (Some(&s[..end.get()]), end.get() + 1), None => (None, 0), }; let (authority, next_of_authority) = match self.authority_end { // +2: "//".len() Some(end) => (Some(&s[(next_of_scheme + 2)..end.get()]), end.get()), None => (None, next_of_scheme), }; let (fragment, end_of_prev_of_fragment) = match self.fragment_start { // -1: "#".len() Some(start) => (Some(&s[start.get()..]), start.get() - 1), None => (None, s.len()), }; let (query, end_of_path) = match self.query_start { Some(start) => ( Some(&s[start.get()..end_of_prev_of_fragment]), // -1: "?".len() start.get() - 1, ), None => (None, end_of_prev_of_fragment), }; let path = &s[next_of_authority..end_of_path]; (scheme, authority, path, query, fragment) } /// Returns the range for the scheme part. #[inline] #[must_use] fn scheme_range(self) -> Option> { self.scheme_end.map(|end| ..end.get()) } /// Returns the scheme as a string. #[inline] #[must_use] pub(crate) fn scheme_str<'a>(&self, s: &'a str) -> Option<&'a str> { self.scheme_range().map(|range| &s[range]) } /// Returns true if the IRI has a scheme part, false otherwise. #[inline] #[must_use] pub(crate) fn has_scheme(&self) -> bool { self.scheme_end.is_some() } /// Returns the range for the authority part. #[inline] #[must_use] fn authority_range(self) -> Option> { let end = self.authority_end?.get(); // 2: "//".len() // +3: "://".len() let start = self.scheme_end.map_or(2, |v| v.get() + 3); Some(start..end) } /// Returns the authority as a string. #[inline] #[must_use] pub(crate) fn authority_str<'a>(&self, s: &'a str) -> Option<&'a str> { self.authority_range().map(|range| &s[range]) } /// Returns true if the IRI has an authority part, false otherwise. #[inline] #[must_use] pub(crate) fn has_authority(&self) -> bool { self.authority_end.is_some() } /// Returns the range for the path part. #[inline] #[must_use] fn path_range(self, full_len: usize) -> Range { // -1: "?".len() and "#".len() let end = self .query_start .or(self.fragment_start) .map_or(full_len, |v| v.get() - 1); let start = self.authority_end.map_or_else( // +1: ":".len() || self.scheme_end.map_or(0, |v| v.get() + 1), NonZeroUsize::get, ); start..end } /// Returns the path as a string. #[inline] #[must_use] pub(crate) fn path_str<'a>(&self, s: &'a str) -> &'a str { &s[self.path_range(s.len())] } /// Returns true if the path part of the IRI is empty. #[inline] #[must_use] pub(crate) fn is_path_empty(&self, full_len: usize) -> bool { self.path_range(full_len).is_empty() } /// Returns the range for the query part excluding a prefix `?`. #[inline] #[must_use] fn query_range(self, full_len: usize) -> Option> { let start = self.query_start?.get(); // -1: "#".len() let end = self.fragment_start.map_or(full_len, |v| v.get() - 1); Some(start..end) } /// Returns the query as a string. #[inline] #[must_use] pub(crate) fn query_str<'a>(&self, s: &'a str) -> Option<&'a str> { self.query_range(s.len()).map(|range| &s[range]) } /// Returns true if the IRI has a query part, false otherwise. #[inline] #[must_use] pub(crate) fn has_query(&self) -> bool { self.query_start.is_some() } /// Returns the range for the fragment part excluding a prefix `#`. #[inline] #[must_use] pub(crate) fn fragment_range(self) -> Option> { self.fragment_start.map(|v| v.get()..) } /// Returns the fragment as a string. #[inline] #[must_use] pub(crate) fn fragment_str<'a>(&self, s: &'a str) -> Option<&'a str> { self.fragment_range().map(|range| &s[range]) } } /// Components of an IRI reference. /// /// See . #[derive(Debug, Clone, Copy)] pub(crate) struct RiReferenceComponents<'a, S: Spec> { /// Original complete string. pub(crate) iri: &'a RiReferenceStr, /// Positions to split the IRI into components. pub(crate) splitter: Splitter, } impl<'a, S: Spec> RiReferenceComponents<'a, S> { /// Returns five major components: scheme, authority, path, query, and fragment. #[inline] #[must_use] pub(crate) fn to_major( self, ) -> ( Option<&'a str>, Option<&'a str>, &'a str, Option<&'a str>, Option<&'a str>, ) { self.splitter.split_into_major(self.iri.as_str()) } /// Returns the IRI reference. #[inline] #[must_use] pub(crate) fn iri(&self) -> &'a RiReferenceStr { self.iri } /// Returns the scheme as a string. #[inline] #[must_use] pub(crate) fn scheme_str(&self) -> Option<&str> { self.splitter.scheme_str(self.iri.as_str()) } /// Returns the authority as a string. #[inline] #[must_use] pub(crate) fn authority_str(&self) -> Option<&str> { self.splitter.authority_str(self.iri.as_str()) } /// Returns the path as a string. #[inline] #[must_use] pub(crate) fn path_str(&self) -> &str { self.splitter.path_str(self.iri.as_str()) } /// Returns the query as a string. #[inline] #[must_use] pub(crate) fn query_str(&self) -> Option<&str> { self.splitter.query_str(self.iri.as_str()) } } impl<'a, S: Spec> From<&'a RiReferenceStr> for RiReferenceComponents<'a, S> { #[inline] fn from(s: &'a RiReferenceStr) -> Self { trusted_parser::decompose_iri_reference(s) } }