Skip to content

Commit f6d6e34

Browse files
committed
feat: implemented Uri
1 parent c1d1cb4 commit f6d6e34

File tree

5 files changed

+942
-82
lines changed

5 files changed

+942
-82
lines changed

header-plz/src/scheme.rs

Lines changed: 0 additions & 82 deletions
This file was deleted.

header-plz/src/uri/builder.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use super::*;
2+
use crate::{
3+
bytes_str::BytesStr,
4+
uri::{path::PathAndQuery, scheme::Scheme},
5+
};
6+
7+
#[derive(Debug, Default, Clone)]
8+
struct UriParts {
9+
pub scheme: Option<Scheme>,
10+
pub authority: Option<BytesStr>,
11+
pub path_and_query: Option<PathAndQuery>,
12+
}
13+
14+
impl From<Uri> for UriParts {
15+
fn from(src: Uri) -> Self {
16+
let path_and_query = if src.has_path() {
17+
Some(src.path_and_query)
18+
} else {
19+
None
20+
};
21+
22+
let scheme = match src.scheme {
23+
Scheme::None => None,
24+
_ => Some(src.scheme),
25+
};
26+
27+
let authority = if src.authority.is_empty() {
28+
None
29+
} else {
30+
Some(src.authority)
31+
};
32+
33+
UriParts {
34+
scheme,
35+
authority,
36+
path_and_query,
37+
}
38+
}
39+
}
40+
41+
#[derive(Debug)]
42+
pub struct Builder(Result<UriParts, InvalidUri>);
43+
44+
impl Builder {
45+
#[inline]
46+
pub fn new() -> Builder {
47+
Builder(Ok(UriParts::default()))
48+
}
49+
50+
pub fn scheme<T>(self, scheme: T) -> Self
51+
where
52+
T: TryInto<Scheme>,
53+
<T as TryInto<Scheme>>::Error: Into<InvalidUri>,
54+
{
55+
self.map(move |mut uri| {
56+
uri.scheme = scheme.try_into().map_err(Into::into)?.into();
57+
Ok(uri)
58+
})
59+
}
60+
61+
pub fn path_and_query<T>(self, scheme: T) -> Self
62+
where
63+
T: TryInto<PathAndQuery>,
64+
<T as TryInto<PathAndQuery>>::Error: Into<InvalidUri>,
65+
{
66+
self.map(move |mut uri| {
67+
uri.path_and_query = scheme.try_into().map_err(Into::into)?.into();
68+
Ok(uri)
69+
})
70+
}
71+
72+
pub fn authority<T>(self, authority: T) -> Self
73+
where
74+
T: TryInto<BytesStr>,
75+
{
76+
self.map(move |mut uri| {
77+
uri.authority = authority
78+
.try_into()
79+
.map_err(|_| InvalidUri::Authority)?
80+
.into();
81+
Ok(uri)
82+
})
83+
}
84+
85+
fn map<F>(self, func: F) -> Self
86+
where
87+
F: FnOnce(UriParts) -> Result<UriParts, InvalidUri>,
88+
{
89+
Builder(self.0.and_then(func))
90+
}
91+
92+
fn build(self) -> Result<Uri, InvalidUri> {
93+
let parts = self.0?;
94+
let scheme = match parts.scheme {
95+
Some(scheme) => scheme,
96+
None => Scheme::None,
97+
};
98+
99+
let authority = match parts.authority {
100+
Some(authority) => authority,
101+
None => BytesStr::new(),
102+
};
103+
104+
let path_and_query = match parts.path_and_query {
105+
Some(path_and_query) => path_and_query,
106+
None => PathAndQuery::empty(),
107+
};
108+
Ok(Uri {
109+
scheme,
110+
authority,
111+
path_and_query,
112+
})
113+
}
114+
}
115+
116+
impl From<Uri> for Builder {
117+
fn from(uri: Uri) -> Self {
118+
Self(Ok(uri.into()))
119+
}
120+
}
121+
122+
#[cfg(test)]
123+
mod tests {
124+
use super::*;
125+
126+
#[test]
127+
fn build_from_str() {
128+
let uri = Builder::new()
129+
.scheme(Scheme::HTTP)
130+
.authority("hyper.rs")
131+
.path_and_query("/foo?a=1#23")
132+
.build()
133+
.unwrap();
134+
assert_eq!(uri.scheme(), Some(&Scheme::HTTP));
135+
assert_eq!(uri.authority().unwrap(), "hyper.rs");
136+
assert_eq!(uri.path(), "/foo");
137+
assert_eq!(uri.query(), Some("a=1"));
138+
}
139+
140+
#[test]
141+
fn build_from_string() {
142+
for i in 1..10 {
143+
let uri = Builder::new()
144+
.path_and_query(format!("/foo?a={}#i", i))
145+
.build()
146+
.unwrap();
147+
let expected_query = format!("a={}", i);
148+
assert_eq!(uri.path(), "/foo");
149+
assert_eq!(uri.query(), Some(expected_query.as_str()));
150+
}
151+
}
152+
153+
#[test]
154+
fn build_from_string_ref() {
155+
for i in 1..10 {
156+
let p_a_q = format!("/foo?a={}", i);
157+
let uri = Builder::new().path_and_query(&p_a_q).build().unwrap();
158+
let expected_query = format!("a={}", i);
159+
assert_eq!(uri.path(), "/foo");
160+
assert_eq!(uri.query(), Some(expected_query.as_str()));
161+
}
162+
}
163+
164+
#[test]
165+
fn build_from_uri() {
166+
let original_uri = Uri::default();
167+
let uri = Builder::from(original_uri.clone()).build().unwrap();
168+
assert_eq!(original_uri, uri);
169+
}
170+
}

header-plz/src/uri/mod.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::{
2+
bytes_str::BytesStr,
3+
uri::{builder::Builder, path::PathAndQuery, scheme::Scheme},
4+
};
5+
use bytes::Bytes;
6+
use std::{convert::Infallible, fmt, str::FromStr};
7+
mod builder;
8+
mod path;
9+
mod scheme;
10+
11+
#[derive(Debug)]
12+
pub enum InvalidUri {
13+
InvalidScheme,
14+
InvalidPath,
15+
InvalidAuthority,
16+
Authority,
17+
Empty,
18+
}
19+
20+
impl From<Infallible> for InvalidUri {
21+
fn from(value: Infallible) -> Self {
22+
match value {}
23+
}
24+
}
25+
26+
#[derive(Debug, Clone)]
27+
pub struct Uri {
28+
scheme: Scheme,
29+
authority: BytesStr,
30+
path_and_query: PathAndQuery,
31+
}
32+
33+
impl Default for Uri {
34+
#[inline]
35+
fn default() -> Uri {
36+
Uri {
37+
scheme: Scheme::empty(),
38+
authority: BytesStr::new(),
39+
path_and_query: PathAndQuery::slash(),
40+
}
41+
}
42+
}
43+
44+
impl Uri {
45+
pub fn builder() -> Builder {
46+
Builder::new()
47+
}
48+
49+
pub fn scheme(&self) -> Option<&Scheme> {
50+
if self.scheme.is_none() {
51+
None
52+
} else {
53+
Some(&self.scheme)
54+
}
55+
}
56+
57+
pub fn path_and_query(&self) -> &PathAndQuery {
58+
&self.path_and_query
59+
}
60+
61+
pub fn path(&self) -> &str {
62+
self.path_and_query.path()
63+
}
64+
65+
pub fn query(&self) -> Option<&str> {
66+
self.path_and_query.query()
67+
}
68+
69+
pub fn authority(&self) -> Option<&str> {
70+
if self.authority.is_empty() {
71+
None
72+
} else {
73+
Some(&self.authority)
74+
}
75+
}
76+
77+
fn has_path(&self) -> bool {
78+
!self.path_and_query.data.is_empty() || !self.scheme.is_none()
79+
}
80+
}
81+
82+
impl PartialEq for Uri {
83+
fn eq(&self, other: &Uri) -> bool {
84+
if self.scheme() != other.scheme() {
85+
return false;
86+
}
87+
88+
if self.authority() != other.authority() {
89+
return false;
90+
}
91+
92+
if self.path() != other.path() {
93+
return false;
94+
}
95+
96+
if self.query() != other.query() {
97+
return false;
98+
}
99+
100+
true
101+
}
102+
}

0 commit comments

Comments
 (0)