Skip to content

Added query and fragment to RouteUri. #737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 47 additions & 28 deletions swimos_utilities/swimos_route/src/route_uri/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ use std::{

use nom::Finish;

use self::parser::RouteUriParts;

mod parser;

#[cfg(test)]
Expand All @@ -32,15 +30,19 @@ mod tests;
pub struct RouteUri {
representation: String,
scheme_len: Option<usize>,
path_len: usize,
path: (usize, usize),
query: Option<(usize, usize)>,
fragment: Option<usize>,
}

impl Default for RouteUri {
fn default() -> Self {
Self {
representation: ".".to_string(),
scheme_len: None,
path_len: 1,
path: (0, 1),
query: None,
fragment: None,
}
}
}
Expand All @@ -52,11 +54,19 @@ impl Display for RouteUri {
}

impl RouteUri {
fn new(original: String, scheme_len: Option<usize>, path_len: usize) -> Self {
fn new(
original: String,
scheme_len: Option<usize>,
path: (usize, usize),
query: Option<(usize, usize)>,
fragment: Option<usize>,
) -> Self {
RouteUri {
representation: original,
scheme_len,
path_len,
path,
query,
fragment,
}
}

Expand All @@ -74,24 +84,31 @@ impl RouteUri {
pub fn path(&self) -> &str {
let RouteUri {
representation,
scheme_len,
path_len,
path,
..
} = self;
let offset = if let Some(n) = scheme_len { *n + 1 } else { 0 };
let end = offset + *path_len;
&representation[offset..end]
let (offset, len) = *path;
&representation[offset..(offset + len)]
}

/// The query of the URI.
/// TODO Implement this.
pub fn query(&self) -> Option<&str> {
None
let RouteUri {
representation,
query,
..
} = self;
(*query).map(|(offset, len)| &representation[offset..(offset + len)])
}

/// The fragment of the URI.
/// TODO Implement this.
pub fn fragment(&self) -> Option<&str> {
None
let RouteUri {
representation,
fragment,
..
} = self;
(*fragment).map(|offset| &representation[offset..])
}

pub fn as_str(&self) -> &str {
Expand Down Expand Up @@ -122,13 +139,6 @@ impl Display for InvalidRouteUri {

impl std::error::Error for InvalidRouteUri {}

fn to_offsets(parts: RouteUriParts<'_>) -> (Option<usize>, usize) {
let RouteUriParts { scheme, path } = parts;
let scheme_len = scheme.map(|span| span.len());
let path_len = path.len();
(scheme_len, path_len)
}

impl FromStr for RouteUri {
type Err = InvalidRouteUri;

Expand All @@ -137,8 +147,13 @@ impl FromStr for RouteUri {
parser::route_uri(span)
.finish()
.map(|(_, parts)| {
let (scheme_len, path_len) = to_offsets(parts);
RouteUri::new(s.to_owned(), scheme_len, path_len)
RouteUri::new(
s.to_owned(),
parts.scheme_len(),
parts.path(),
parts.query(),
parts.fragment_offset(),
)
})
.map_err(|_| InvalidRouteUri(s.to_owned()))
}
Expand All @@ -157,11 +172,15 @@ impl TryFrom<String> for RouteUri {

fn try_from(value: String) -> Result<Self, Self::Error> {
let span = parser::Span::new(value.as_str());
let result = parser::route_uri(span)
.finish()
.map(|(_, parts)| to_offsets(parts));
let result = parser::route_uri(span).finish().map(|(_, parts)| parts);
match result {
Ok((scheme_len, path_len)) => Ok(RouteUri::new(value, scheme_len, path_len)),
Ok(parts) => {
let scheme = parts.scheme_len();
let path = parts.path();
let query = parts.query();
let fragment = parts.fragment_offset();
Ok(RouteUri::new(value, scheme, path, query, fragment))
}
_ => Err(InvalidRouteUri(value)),
}
}
Expand Down
52 changes: 50 additions & 2 deletions swimos_utilities/swimos_route/src/route_uri/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ fn is_path_char(c: char) -> bool {
|| c == '@'
|| c == '&'
|| c == '='
|| c == ';'
}

fn is_query_or_fragment_char(c: char) -> bool {
is_path_char(c) || c == '/' || c == '?'
}

fn hex(input: Span<'_>) -> IResult<Span<'_>, char> {
Expand All @@ -64,10 +69,28 @@ fn path_char(input: Span<'_>) -> IResult<Span<'_>, Span<'_>> {
alt((recognize(satisfy(is_path_char)), escape))(input)
}

fn query_or_fragment_char(input: Span<'_>) -> IResult<Span<'_>, Span<'_>> {
alt((recognize(satisfy(is_query_or_fragment_char)), escape))(input)
}

fn path_segment(input: Span<'_>) -> IResult<Span<'_>, usize> {
many0_count(path_char)(input)
}

fn query(input: Span<'_>) -> IResult<Span<'_>, Option<Span<'_>>> {
opt(preceded(
char('?'),
recognize(many0_count(query_or_fragment_char)),
))(input)
}

fn fragment(input: Span<'_>) -> IResult<Span<'_>, Option<Span<'_>>> {
opt(preceded(
char('#'),
recognize(many0_count(query_or_fragment_char)),
))(input)
}

fn first_path_segment(input: Span<'_>) -> IResult<Span<'_>, usize> {
many1_count(path_char)(input)
}
Expand All @@ -86,11 +109,36 @@ fn path(input: Span<'_>) -> IResult<Span<'_>, Span<'_>> {
pub struct RouteUriParts<'a> {
pub scheme: Option<Span<'a>>,
pub path: Span<'a>,
pub query: Option<Span<'a>>,
pub fragment: Option<Span<'a>>,
}

impl<'a> RouteUriParts<'a> {
pub fn scheme_len(&self) -> Option<usize> {
self.scheme.map(|span| span.len())
}

pub fn path(&self) -> (usize, usize) {
(self.path.location_offset(), self.path.len())
}

pub fn query(&self) -> Option<(usize, usize)> {
self.query.map(|span| (span.location_offset(), span.len()))
}

pub fn fragment_offset(&self) -> Option<usize> {
self.fragment.map(|span| span.location_offset())
}
}

pub fn route_uri(input: Span<'_>) -> IResult<Span<'_>, RouteUriParts<'_>> {
map(
pair(opt(terminated(scheme, char(':'))), path),
|(scheme, path)| RouteUriParts { scheme, path },
tuple((opt(terminated(scheme, char(':'))), path, query, fragment)),
|(scheme, path, query, fragment)| RouteUriParts {
scheme,
path,
query,
fragment,
},
)(input)
}
27 changes: 27 additions & 0 deletions swimos_utilities/swimos_route/src/route_uri/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,33 @@ fn route_uri_parse_good() {
} else {
panic!("Bad route URI.");
}

if let Ok(route_uri) = "swimos:/example?a=2&b=3".parse::<RouteUri>() {
assert_eq!(route_uri.scheme(), Some("swimos"));
assert_eq!(route_uri.path(), "/example");
assert_eq!(route_uri.query(), Some("a=2&b=3"));
assert!(route_uri.fragment().is_none());
} else {
panic!("Bad route URI.");
}

if let Ok(route_uri) = "swimos:/example#target".parse::<RouteUri>() {
assert_eq!(route_uri.scheme(), Some("swimos"));
assert_eq!(route_uri.path(), "/example");
assert!(route_uri.query().is_none());
assert_eq!(route_uri.fragment(), Some("target"));
} else {
panic!("Bad route URI.");
}

if let Ok(route_uri) = "swimos:/example?a=2&b=3#target".parse::<RouteUri>() {
assert_eq!(route_uri.scheme(), Some("swimos"));
assert_eq!(route_uri.path(), "/example");
assert_eq!(route_uri.query(), Some("a=2&b=3"));
assert_eq!(route_uri.fragment(), Some("target"));
} else {
panic!("Bad route URI.");
}
}

#[test]
Expand Down
Loading