use std::fmt::{self, Display};
use std::borrow::Cow;
use ext::IntoOwned;
use parse::{Indexed, IndexedStr};
use uri::{as_utf8_unchecked, Error, Segments};
use state::Storage;
#[derive(Clone, Debug)]
pub struct Origin<'a> {
pub(crate) source: Option<Cow<'a, str>>,
pub(crate) path: IndexedStr<'a>,
pub(crate) query: Option<IndexedStr<'a>>,
pub(crate) segment_count: Storage<usize>,
}
impl<'a, 'b> PartialEq<Origin<'b>> for Origin<'a> {
fn eq(&self, other: &Origin<'b>) -> bool {
self.path() == other.path() && self.query() == other.query()
}
}
impl<'a> IntoOwned for Origin<'a> {
type Owned = Origin<'static>;
fn into_owned(self) -> Origin<'static> {
Origin {
source: self.source.into_owned(),
path: self.path.into_owned(),
query: self.query.into_owned(),
segment_count: self.segment_count
}
}
}
impl<'a> Origin<'a> {
#[inline]
pub(crate) unsafe fn raw(
source: Cow<'a, [u8]>,
path: Indexed<'a, [u8]>,
query: Option<Indexed<'a, [u8]>>
) -> Origin<'a> {
Origin {
source: Some(as_utf8_unchecked(source)),
path: path.coerce(),
query: query.map(|q| q.coerce()),
segment_count: Storage::new()
}
}
#[doc(hidden)]
pub fn new<P, Q>(path: P, query: Option<Q>) -> Origin<'a>
where P: Into<Cow<'a, str>>, Q: Into<Cow<'a, str>>
{
Origin {
source: None,
path: Indexed::from(path),
query: query.map(Indexed::from),
segment_count: Storage::new()
}
}
#[doc(hidden)]
pub fn dummy() -> Origin<'static> {
Origin::new::<&'static str, &'static str>("/", None)
}
pub fn parse(string: &'a str) -> Result<Origin<'a>, Error<'a>> {
::parse::uri::origin_from_str(string)
}
#[doc(hidden)]
pub fn parse_route(string: &'a str) -> Result<Origin<'a>, Error<'a>> {
::parse::uri::route_origin_from_str(string)
}
pub fn parse_owned(string: String) -> Result<Origin<'static>, Error<'static>> {
let origin = Origin::parse(&string).map_err(|e| e.into_owned())?;
debug_assert!(origin.source.is_some(), "Origin source parsed w/o source");
Ok(Origin {
path: origin.path.into_owned(),
query: origin.query.into_owned(),
segment_count: origin.segment_count,
source: Some(Cow::Owned(string))
})
}
pub fn is_normalized(&self) -> bool {
self.path().starts_with('/') &&
!self.path().contains("//") &&
!(self.path().len() > 1 && self.path().ends_with('/'))
}
pub fn to_normalized(&self) -> Origin {
if self.is_normalized() {
Origin::new(self.path(), self.query())
} else {
let mut new_path = String::with_capacity(self.path().len());
for segment in self.segments() {
use std::fmt::Write;
let _ = write!(new_path, "/{}", segment);
}
if new_path.is_empty() {
new_path.push('/');
}
Origin::new(new_path, self.query())
}
}
#[inline]
pub fn path(&self) -> &str {
self.path.from_cow_source(&self.source)
}
#[inline]
pub fn map_path<F: FnOnce(&str) -> String>(&self, f: F) -> Option<Self> {
let path = f(self.path());
if !path.starts_with('/') || !path.bytes().all(crate::parse::uri::is_pchar) {
return None;
}
Some(Origin {
source: self.source.clone(),
path: path.into(),
query: self.query.clone(),
segment_count: Storage::new(),
})
}
#[inline]
pub fn query(&self) -> Option<&str> {
self.query.as_ref().map(|q| q.from_cow_source(&self.source))
}
pub fn clear_query(&mut self) {
self.query = None;
}
#[inline(always)]
pub fn segments(&self) -> Segments {
Segments(self.path())
}
#[inline]
pub fn segment_count(&self) -> usize {
*self.segment_count.get_or_set(|| self.segments().count())
}
}
impl<'a> Display for Origin<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.path())?;
if let Some(q) = self.query() {
write!(f, "?{}", q)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::Origin;
fn seg_count(path: &str, expected: usize) -> bool {
let actual = Origin::parse(path).unwrap().segment_count();
if actual != expected {
eprintln!("Count mismatch: expected {}, got {}.", expected, actual);
eprintln!("{}", if actual != expected { "lifetime" } else { "buf" });
eprintln!("Segments (for {}):", path);
for (i, segment) in Origin::parse(path).unwrap().segments().enumerate() {
eprintln!("{}: {}", i, segment);
}
}
actual == expected
}
fn eq_segments(path: &str, expected: &[&str]) -> bool {
let uri = match Origin::parse(path) {
Ok(uri) => uri,
Err(e) => panic!("failed to parse {}: {}", path, e)
};
let actual: Vec<&str> = uri.segments().collect();
actual == expected
}
#[test]
fn send_and_sync() {
fn assert<T: Send + Sync>() {}
assert::<Origin>();
}
#[test]
fn simple_segment_count() {
assert!(seg_count("/", 0));
assert!(seg_count("/a", 1));
assert!(seg_count("/a/", 1));
assert!(seg_count("/a/", 1));
assert!(seg_count("/a/b", 2));
assert!(seg_count("/a/b/", 2));
assert!(seg_count("/a/b/", 2));
assert!(seg_count("/ab/", 1));
}
#[test]
fn segment_count() {
assert!(seg_count("////", 0));
assert!(seg_count("//a//", 1));
assert!(seg_count("//abc//", 1));
assert!(seg_count("//abc/def/", 2));
assert!(seg_count("//////abc///def//////////", 2));
assert!(seg_count("/a/b/c/d/e/f/g", 7));
assert!(seg_count("/a/b/c/d/e/f/g", 7));
assert!(seg_count("/a/b/c/d/e/f/g/", 7));
assert!(seg_count("/a/b/cdjflk/d/e/f/g", 7));
assert!(seg_count("//aaflja/b/cdjflk/d/e/f/g", 7));
assert!(seg_count("/a/b", 2));
}
#[test]
fn single_segments_match() {
assert!(eq_segments("/", &[]));
assert!(eq_segments("/a", &["a"]));
assert!(eq_segments("/a/", &["a"]));
assert!(eq_segments("///a/", &["a"]));
assert!(eq_segments("///a///////", &["a"]));
assert!(eq_segments("/a///////", &["a"]));
assert!(eq_segments("//a", &["a"]));
assert!(eq_segments("/abc", &["abc"]));
assert!(eq_segments("/abc/", &["abc"]));
assert!(eq_segments("///abc/", &["abc"]));
assert!(eq_segments("///abc///////", &["abc"]));
assert!(eq_segments("/abc///////", &["abc"]));
assert!(eq_segments("//abc", &["abc"]));
}
#[test]
fn multi_segments_match() {
assert!(eq_segments("/a/b/c", &["a", "b", "c"]));
assert!(eq_segments("/a/b", &["a", "b"]));
assert!(eq_segments("/a///b", &["a", "b"]));
assert!(eq_segments("/a/b/c/d", &["a", "b", "c", "d"]));
assert!(eq_segments("///a///////d////c", &["a", "d", "c"]));
assert!(eq_segments("/abc/abc", &["abc", "abc"]));
assert!(eq_segments("/abc/abc/", &["abc", "abc"]));
assert!(eq_segments("///abc///////a", &["abc", "a"]));
assert!(eq_segments("/////abc/b", &["abc", "b"]));
assert!(eq_segments("//abc//c////////d", &["abc", "c", "d"]));
}
#[test]
fn multi_segments_match_funky_chars() {
assert!(eq_segments("/a/b/c!!!", &["a", "b", "c!!!"]));
}
#[test]
fn segment_mismatch() {
assert!(!eq_segments("/", &["a"]));
assert!(!eq_segments("/a", &[]));
assert!(!eq_segments("/a/a", &["a"]));
assert!(!eq_segments("/a/b", &["b", "a"]));
assert!(!eq_segments("/a/a/b", &["a", "b"]));
assert!(!eq_segments("///a/", &[]));
}
fn test_query(uri: &str, query: Option<&str>) {
let uri = Origin::parse(uri).unwrap();
assert_eq!(uri.query(), query);
}
#[test]
fn query_does_not_exist() {
test_query("/test", None);
test_query("/a/b/c/d/e", None);
test_query("/////", None);
test_query("//a///", None);
test_query("/a/b/c", None);
test_query("/", None);
}
#[test]
fn query_exists() {
test_query("/test?abc", Some("abc"));
test_query("/a/b/c?abc", Some("abc"));
test_query("/a/b/c/d/e/f/g/?abc", Some("abc"));
test_query("/?123", Some("123"));
test_query("/?", Some(""));
test_query("/?", Some(""));
test_query("/?hi", Some("hi"));
}
#[test]
fn normalized() {
let uri_to_string = |s| Origin::parse(s)
.unwrap()
.to_normalized()
.to_string();
assert_eq!(uri_to_string("/"), "/".to_string());
assert_eq!(uri_to_string("//"), "/".to_string());
assert_eq!(uri_to_string("//////a/"), "/a".to_string());
assert_eq!(uri_to_string("//ab"), "/ab".to_string());
assert_eq!(uri_to_string("//a"), "/a".to_string());
assert_eq!(uri_to_string("/a/b///c"), "/a/b/c".to_string());
assert_eq!(uri_to_string("/a///b/c/d///"), "/a/b/c/d".to_string());
}
}