Skip to content

Commit 8da461a

Browse files
Merge branch 'main' into cleanup
2 parents fa8199e + dc696f2 commit 8da461a

File tree

3 files changed

+124
-30
lines changed

3 files changed

+124
-30
lines changed

swimos_utilities/swimos_route/src/route_uri/mod.rs

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ use std::{
2020

2121
use nom::Finish;
2222

23-
use self::parser::RouteUriParts;
24-
2523
mod parser;
2624

2725
#[cfg(test)]
@@ -32,15 +30,19 @@ mod tests;
3230
pub struct RouteUri {
3331
representation: String,
3432
scheme_len: Option<usize>,
35-
path_len: usize,
33+
path: (usize, usize),
34+
query: Option<(usize, usize)>,
35+
fragment: Option<usize>,
3636
}
3737

3838
impl Default for RouteUri {
3939
fn default() -> Self {
4040
Self {
4141
representation: ".".to_string(),
4242
scheme_len: None,
43-
path_len: 1,
43+
path: (0, 1),
44+
query: None,
45+
fragment: None,
4446
}
4547
}
4648
}
@@ -52,11 +54,19 @@ impl Display for RouteUri {
5254
}
5355

5456
impl RouteUri {
55-
fn new(original: String, scheme_len: Option<usize>, path_len: usize) -> Self {
57+
fn new(
58+
original: String,
59+
scheme_len: Option<usize>,
60+
path: (usize, usize),
61+
query: Option<(usize, usize)>,
62+
fragment: Option<usize>,
63+
) -> Self {
5664
RouteUri {
5765
representation: original,
5866
scheme_len,
59-
path_len,
67+
path,
68+
query,
69+
fragment,
6070
}
6171
}
6272

@@ -74,24 +84,31 @@ impl RouteUri {
7484
pub fn path(&self) -> &str {
7585
let RouteUri {
7686
representation,
77-
scheme_len,
78-
path_len,
87+
path,
88+
..
7989
} = self;
80-
let offset = if let Some(n) = scheme_len { *n + 1 } else { 0 };
81-
let end = offset + *path_len;
82-
&representation[offset..end]
90+
let (offset, len) = *path;
91+
&representation[offset..(offset + len)]
8392
}
8493

8594
/// The query of the URI.
86-
/// TODO Implement this.
8795
pub fn query(&self) -> Option<&str> {
88-
None
96+
let RouteUri {
97+
representation,
98+
query,
99+
..
100+
} = self;
101+
(*query).map(|(offset, len)| &representation[offset..(offset + len)])
89102
}
90103

91104
/// The fragment of the URI.
92-
/// TODO Implement this.
93105
pub fn fragment(&self) -> Option<&str> {
94-
None
106+
let RouteUri {
107+
representation,
108+
fragment,
109+
..
110+
} = self;
111+
(*fragment).map(|offset| &representation[offset..])
95112
}
96113

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

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

125-
fn to_offsets(parts: RouteUriParts<'_>) -> (Option<usize>, usize) {
126-
let RouteUriParts { scheme, path } = parts;
127-
let scheme_len = scheme.map(|span| span.len());
128-
let path_len = path.len();
129-
(scheme_len, path_len)
130-
}
131-
132142
impl FromStr for RouteUri {
133143
type Err = InvalidRouteUri;
134144

@@ -137,8 +147,13 @@ impl FromStr for RouteUri {
137147
parser::route_uri(span)
138148
.finish()
139149
.map(|(_, parts)| {
140-
let (scheme_len, path_len) = to_offsets(parts);
141-
RouteUri::new(s.to_owned(), scheme_len, path_len)
150+
RouteUri::new(
151+
s.to_owned(),
152+
parts.scheme_len(),
153+
parts.path(),
154+
parts.query(),
155+
parts.fragment_offset(),
156+
)
142157
})
143158
.map_err(|_| InvalidRouteUri(s.to_owned()))
144159
}
@@ -157,11 +172,15 @@ impl TryFrom<String> for RouteUri {
157172

158173
fn try_from(value: String) -> Result<Self, Self::Error> {
159174
let span = parser::Span::new(value.as_str());
160-
let result = parser::route_uri(span)
161-
.finish()
162-
.map(|(_, parts)| to_offsets(parts));
175+
let result = parser::route_uri(span).finish().map(|(_, parts)| parts);
163176
match result {
164-
Ok((scheme_len, path_len)) => Ok(RouteUri::new(value, scheme_len, path_len)),
177+
Ok(parts) => {
178+
let scheme = parts.scheme_len();
179+
let path = parts.path();
180+
let query = parts.query();
181+
let fragment = parts.fragment_offset();
182+
Ok(RouteUri::new(value, scheme, path, query, fragment))
183+
}
165184
_ => Err(InvalidRouteUri(value)),
166185
}
167186
}

swimos_utilities/swimos_route/src/route_uri/parser/mod.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ fn is_path_char(c: char) -> bool {
5050
|| c == '@'
5151
|| c == '&'
5252
|| c == '='
53+
|| c == ';'
54+
}
55+
56+
fn is_query_or_fragment_char(c: char) -> bool {
57+
is_path_char(c) || c == '/' || c == '?'
5358
}
5459

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

72+
fn query_or_fragment_char(input: Span<'_>) -> IResult<Span<'_>, Span<'_>> {
73+
alt((recognize(satisfy(is_query_or_fragment_char)), escape))(input)
74+
}
75+
6776
fn path_segment(input: Span<'_>) -> IResult<Span<'_>, usize> {
6877
many0_count(path_char)(input)
6978
}
7079

80+
fn query(input: Span<'_>) -> IResult<Span<'_>, Option<Span<'_>>> {
81+
opt(preceded(
82+
char('?'),
83+
recognize(many0_count(query_or_fragment_char)),
84+
))(input)
85+
}
86+
87+
fn fragment(input: Span<'_>) -> IResult<Span<'_>, Option<Span<'_>>> {
88+
opt(preceded(
89+
char('#'),
90+
recognize(many0_count(query_or_fragment_char)),
91+
))(input)
92+
}
93+
7194
fn first_path_segment(input: Span<'_>) -> IResult<Span<'_>, usize> {
7295
many1_count(path_char)(input)
7396
}
@@ -86,11 +109,36 @@ fn path(input: Span<'_>) -> IResult<Span<'_>, Span<'_>> {
86109
pub struct RouteUriParts<'a> {
87110
pub scheme: Option<Span<'a>>,
88111
pub path: Span<'a>,
112+
pub query: Option<Span<'a>>,
113+
pub fragment: Option<Span<'a>>,
114+
}
115+
116+
impl<'a> RouteUriParts<'a> {
117+
pub fn scheme_len(&self) -> Option<usize> {
118+
self.scheme.map(|span| span.len())
119+
}
120+
121+
pub fn path(&self) -> (usize, usize) {
122+
(self.path.location_offset(), self.path.len())
123+
}
124+
125+
pub fn query(&self) -> Option<(usize, usize)> {
126+
self.query.map(|span| (span.location_offset(), span.len()))
127+
}
128+
129+
pub fn fragment_offset(&self) -> Option<usize> {
130+
self.fragment.map(|span| span.location_offset())
131+
}
89132
}
90133

91134
pub fn route_uri(input: Span<'_>) -> IResult<Span<'_>, RouteUriParts<'_>> {
92135
map(
93-
pair(opt(terminated(scheme, char(':'))), path),
94-
|(scheme, path)| RouteUriParts { scheme, path },
136+
tuple((opt(terminated(scheme, char(':'))), path, query, fragment)),
137+
|(scheme, path, query, fragment)| RouteUriParts {
138+
scheme,
139+
path,
140+
query,
141+
fragment,
142+
},
95143
)(input)
96144
}

swimos_utilities/swimos_route/src/route_uri/tests.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,33 @@ fn route_uri_parse_good() {
5555
} else {
5656
panic!("Bad route URI.");
5757
}
58+
59+
if let Ok(route_uri) = "swimos:/example?a=2&b=3".parse::<RouteUri>() {
60+
assert_eq!(route_uri.scheme(), Some("swimos"));
61+
assert_eq!(route_uri.path(), "/example");
62+
assert_eq!(route_uri.query(), Some("a=2&b=3"));
63+
assert!(route_uri.fragment().is_none());
64+
} else {
65+
panic!("Bad route URI.");
66+
}
67+
68+
if let Ok(route_uri) = "swimos:/example#target".parse::<RouteUri>() {
69+
assert_eq!(route_uri.scheme(), Some("swimos"));
70+
assert_eq!(route_uri.path(), "/example");
71+
assert!(route_uri.query().is_none());
72+
assert_eq!(route_uri.fragment(), Some("target"));
73+
} else {
74+
panic!("Bad route URI.");
75+
}
76+
77+
if let Ok(route_uri) = "swimos:/example?a=2&b=3#target".parse::<RouteUri>() {
78+
assert_eq!(route_uri.scheme(), Some("swimos"));
79+
assert_eq!(route_uri.path(), "/example");
80+
assert_eq!(route_uri.query(), Some("a=2&b=3"));
81+
assert_eq!(route_uri.fragment(), Some("target"));
82+
} else {
83+
panic!("Bad route URI.");
84+
}
5885
}
5986

6087
#[test]

0 commit comments

Comments
 (0)