mirror of
https://github.com/rwf2/Rocket.git
synced 2025-02-16 13:42:05 +00:00
Add fragment support to URI type.
This commit is contained in:
parent
a145a11848
commit
762b38efe9
@ -1,14 +1,19 @@
|
|||||||
|
//! Borrowed and owned string types for absolute URIs.
|
||||||
|
//!
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
use router::Collider;
|
use router::Collider;
|
||||||
|
|
||||||
|
// TODO: Reconsider deriving PartialEq and Eq to make "//a/b" == "/a/b".
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct URI<'a> {
|
pub struct URI<'a> {
|
||||||
uri: &'a str,
|
uri: &'a str,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
query: Option<&'a str>,
|
query: Option<&'a str>,
|
||||||
|
fragment: Option<&'a str>,
|
||||||
segment_count: Cell<Option<usize>>,
|
segment_count: Cell<Option<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,9 +21,15 @@ impl<'a> URI<'a> {
|
|||||||
pub fn new<T: AsRef<str> + ?Sized>(uri: &'a T) -> URI<'a> {
|
pub fn new<T: AsRef<str> + ?Sized>(uri: &'a T) -> URI<'a> {
|
||||||
let uri = uri.as_ref();
|
let uri = uri.as_ref();
|
||||||
|
|
||||||
let (path, query) = match uri.find('?') {
|
let qmark = uri.find('?');
|
||||||
Some(index) => (&uri[..index], Some(&uri[(index + 1)..])),
|
let hmark = qmark.map(|i| uri[(i + 1)..].find('#').map(|j| j + i + 1))
|
||||||
None => (uri, None),
|
.unwrap_or_else(|| uri.find('#'));
|
||||||
|
|
||||||
|
let (path, query, fragment) = match (qmark, hmark) {
|
||||||
|
(Some(i), Some(j)) => (&uri[..i], Some(&uri[(i+1)..j]), Some(&uri[(j+1)..])),
|
||||||
|
(Some(i), None) => (&uri[..i], Some(&uri[(i+1)..]), None),
|
||||||
|
(None, Some(j)) => (&uri[..j], None, Some(&uri[(j+1)..])),
|
||||||
|
(None, None) => (uri, None, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
URI {
|
URI {
|
||||||
@ -26,9 +37,11 @@ impl<'a> URI<'a> {
|
|||||||
uri: uri,
|
uri: uri,
|
||||||
path: path,
|
path: path,
|
||||||
query: query,
|
query: query,
|
||||||
|
fragment: fragment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn segment_count(&self) -> usize {
|
pub fn segment_count(&self) -> usize {
|
||||||
self.segment_count.get().unwrap_or_else(|| {
|
self.segment_count.get().unwrap_or_else(|| {
|
||||||
let count = self.segments().count();
|
let count = self.segments().count();
|
||||||
@ -37,14 +50,22 @@ impl<'a> URI<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn segments(&self) -> Segments<'a> {
|
pub fn segments(&self) -> Segments<'a> {
|
||||||
Segments(self.path)
|
Segments(self.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn query(&self) -> Option<&'a str> {
|
pub fn query(&self) -> Option<&'a str> {
|
||||||
self.query
|
self.query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn fragment(&self) -> Option<&'a str> {
|
||||||
|
self.fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn as_str(&self) -> &'a str {
|
pub fn as_str(&self) -> &'a str {
|
||||||
self.uri
|
self.uri
|
||||||
}
|
}
|
||||||
@ -83,24 +104,29 @@ impl URIBuf {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn segments(&self) -> Segments {
|
pub fn segments(&self) -> Segments {
|
||||||
self.as_uri_uncached().segments()
|
self.as_uri_uncached().segments()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn as_uri_uncached(&self) -> URI {
|
fn as_uri_uncached(&self) -> URI {
|
||||||
URI::new(self.uri.as_str())
|
URI::new(self.uri.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn as_uri(&self) -> URI {
|
pub fn as_uri(&self) -> URI {
|
||||||
let mut uri = URI::new(self.uri.as_str());
|
let mut uri = URI::new(self.uri.as_str());
|
||||||
uri.segment_count = self.segment_count.clone();
|
uri.segment_count = self.segment_count.clone();
|
||||||
uri
|
uri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
self.uri.as_str()
|
self.uri.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
self.uri.clone()
|
self.uri.clone()
|
||||||
}
|
}
|
||||||
@ -115,6 +141,7 @@ impl fmt::Display for URIBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for URIBuf {
|
impl From<String> for URIBuf {
|
||||||
|
#[inline(always)]
|
||||||
fn from(uri: String) -> URIBuf {
|
fn from(uri: String) -> URIBuf {
|
||||||
URIBuf {
|
URIBuf {
|
||||||
segment_count: Cell::new(None),
|
segment_count: Cell::new(None),
|
||||||
@ -124,6 +151,7 @@ impl From<String> for URIBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for URIBuf {
|
impl<'a> From<&'a str> for URIBuf {
|
||||||
|
#[inline(always)]
|
||||||
fn from(uri: &'a str) -> URIBuf {
|
fn from(uri: &'a str) -> URIBuf {
|
||||||
URIBuf {
|
URIBuf {
|
||||||
segment_count: Cell::new(None),
|
segment_count: Cell::new(None),
|
||||||
@ -134,11 +162,9 @@ impl<'a> From<&'a str> for URIBuf {
|
|||||||
|
|
||||||
impl<'a, 'b> Collider<URI<'b>> for URI<'a> {
|
impl<'a, 'b> Collider<URI<'b>> for URI<'a> {
|
||||||
fn collides_with(&self, other: &URI<'b>) -> bool {
|
fn collides_with(&self, other: &URI<'b>) -> bool {
|
||||||
let mut trailing = false;
|
|
||||||
for (seg_a, seg_b) in self.segments().zip(other.segments()) {
|
for (seg_a, seg_b) in self.segments().zip(other.segments()) {
|
||||||
if seg_a.ends_with("..>") || seg_b.ends_with("..>") {
|
if seg_a.ends_with("..>") || seg_b.ends_with("..>") {
|
||||||
trailing = true;
|
return true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !seg_a.collides_with(seg_b) {
|
if !seg_a.collides_with(seg_b) {
|
||||||
@ -146,7 +172,7 @@ impl<'a, 'b> Collider<URI<'b>> for URI<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !trailing && (self.segment_count() != other.segment_count()) {
|
if self.segment_count() != other.segment_count() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,4 +327,50 @@ mod tests {
|
|||||||
assert!(!eq_segments("/a/a/b", &["a", "b"]));
|
assert!(!eq_segments("/a/a/b", &["a", "b"]));
|
||||||
assert!(!eq_segments("///a/", &[]));
|
assert!(!eq_segments("///a/", &[]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_query(uri: &str, query: Option<&str>) {
|
||||||
|
let uri = URI::new(uri);
|
||||||
|
assert_eq!(uri.query(), query);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_fragment(uri: &str, fragment: Option<&str>) {
|
||||||
|
let uri = URI::new(uri);
|
||||||
|
assert_eq!(uri.fragment(), fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]
|
||||||
|
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#hijklmnop", Some("abc"));
|
||||||
|
test_query("?123", Some("123"));
|
||||||
|
test_query("?", Some(""));
|
||||||
|
test_query("/?", Some(""));
|
||||||
|
test_query("?#", Some(""));
|
||||||
|
test_query("/?hi", Some("hi"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragment_exists() {
|
||||||
|
test_fragment("/test#abc", Some("abc"));
|
||||||
|
test_fragment("/#abc", Some("abc"));
|
||||||
|
test_fragment("/a/b/c?123#a", Some("a"));
|
||||||
|
test_fragment("/#a", Some("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fragment_does_not_exist() {
|
||||||
|
test_fragment("/testabc", None);
|
||||||
|
test_fragment("/abc", None);
|
||||||
|
test_fragment("/a/b/c?123", None);
|
||||||
|
test_fragment("/a", None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user