improved template parsing (#111)
This commit is contained in:
parent
236efadeee
commit
6e90509eef
|
@ -27,7 +27,6 @@ impl PathTemplate {
|
||||||
let mut fmt = String::new();
|
let mut fmt = String::new();
|
||||||
fmt.push_str("{}");
|
fmt.push_str("{}");
|
||||||
for c in self.components.iter() {
|
for c in self.components.iter() {
|
||||||
fmt.push('/');
|
|
||||||
match c {
|
match c {
|
||||||
Component::Constant(n) => fmt.push_str(n),
|
Component::Constant(n) => fmt.push_str(n),
|
||||||
Component::Parameter(_) => fmt.push_str("{}"),
|
Component::Parameter(_) => fmt.push_str("{}"),
|
||||||
|
@ -71,7 +70,6 @@ pub fn parse(t: &str) -> Result<PathTemplate> {
|
||||||
Start,
|
Start,
|
||||||
ConstantOrParameter,
|
ConstantOrParameter,
|
||||||
Parameter,
|
Parameter,
|
||||||
ParameterSlash,
|
|
||||||
Constant,
|
Constant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +81,8 @@ pub fn parse(t: &str) -> Result<PathTemplate> {
|
||||||
match s {
|
match s {
|
||||||
State::Start => {
|
State::Start => {
|
||||||
if c == '/' {
|
if c == '/' {
|
||||||
s = State::ConstantOrParameter;
|
s = State::Constant;
|
||||||
|
a = c.to_string();
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::InvalidPath(
|
return Err(Error::InvalidPath(
|
||||||
"path must start with a slash".to_string(),
|
"path must start with a slash".to_string(),
|
||||||
|
@ -91,50 +90,38 @@ pub fn parse(t: &str) -> Result<PathTemplate> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::ConstantOrParameter => {
|
State::ConstantOrParameter => {
|
||||||
if c == '/' || c == '}' {
|
if c == '{' {
|
||||||
return Err(Error::InvalidPath(
|
|
||||||
"expected a constant or parameter".to_string(),
|
|
||||||
));
|
|
||||||
} else if c == '{' {
|
|
||||||
s = State::Parameter;
|
s = State::Parameter;
|
||||||
|
a = String::new();
|
||||||
} else {
|
} else {
|
||||||
s = State::Constant;
|
s = State::Constant;
|
||||||
a.push(c);
|
a = c.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::Constant => {
|
State::Constant => {
|
||||||
if c == '/' {
|
if c == '{' {
|
||||||
components.push(Component::Constant(a));
|
components.push(Component::Constant(a));
|
||||||
a = String::new();
|
a = String::new();
|
||||||
s = State::ConstantOrParameter;
|
s = State::Parameter;
|
||||||
} else if c == '{' || c == '}' {
|
} else if c == '}' {
|
||||||
return Err(Error::InvalidPath(
|
return Err(Error::InvalidPath("unexpected }".to_string()));
|
||||||
"unexpected parameter".to_string(),
|
} else if c != '/' || a.chars().last() != Some('/') {
|
||||||
));
|
|
||||||
} else {
|
|
||||||
a.push(c);
|
a.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::Parameter => {
|
State::Parameter => {
|
||||||
if c == '}' {
|
if c == '}' {
|
||||||
|
if a.contains('/') || a.contains('{') {
|
||||||
|
return Err(Error::InvalidPath(format!(
|
||||||
|
"invalid parameter name {:?}",
|
||||||
|
a,
|
||||||
|
)));
|
||||||
|
}
|
||||||
components.push(Component::Parameter(a));
|
components.push(Component::Parameter(a));
|
||||||
a = String::new();
|
a = String::new();
|
||||||
s = State::ParameterSlash;
|
|
||||||
} else if c == '/' || c == '{' {
|
|
||||||
return Err(Error::InvalidPath(
|
|
||||||
"unexpected parameter".to_string(),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
a.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::ParameterSlash => {
|
|
||||||
if c == '/' {
|
|
||||||
s = State::ConstantOrParameter;
|
s = State::ConstantOrParameter;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::InvalidPath(
|
a.push(c);
|
||||||
"expected a slah after parameter".to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +131,7 @@ pub fn parse(t: &str) -> Result<PathTemplate> {
|
||||||
State::Start => {
|
State::Start => {
|
||||||
return Err(Error::InvalidPath("empty path".to_string()))
|
return Err(Error::InvalidPath("empty path".to_string()))
|
||||||
}
|
}
|
||||||
State::ConstantOrParameter | State::ParameterSlash => (),
|
State::ConstantOrParameter => (),
|
||||||
State::Constant => components.push(Component::Constant(a)),
|
State::Constant => components.push(Component::Constant(a)),
|
||||||
State::Parameter => {
|
State::Parameter => {
|
||||||
return Err(Error::InvalidPath(
|
return Err(Error::InvalidPath(
|
||||||
|
@ -164,7 +151,7 @@ impl ToString for PathTemplate {
|
||||||
Component::Constant(s) => s.clone(),
|
Component::Constant(s) => s.clone(),
|
||||||
Component::Parameter(s) => format!("{{{}}}", s),
|
Component::Parameter(s) => format!("{{{}}}", s),
|
||||||
})
|
})
|
||||||
.fold(String::new(), |a, b| a + "/" + &b)
|
.fold(String::new(), |a, b| a + &b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,35 +165,90 @@ mod test {
|
||||||
fn basic() {
|
fn basic() {
|
||||||
let trials = vec![
|
let trials = vec![
|
||||||
(
|
(
|
||||||
|
"/info",
|
||||||
"/info",
|
"/info",
|
||||||
PathTemplate {
|
PathTemplate {
|
||||||
components: vec![Component::Constant("info".into())],
|
components: vec![Component::Constant("/info".into())],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
"/measure/{number}",
|
||||||
"/measure/{number}",
|
"/measure/{number}",
|
||||||
PathTemplate {
|
PathTemplate {
|
||||||
components: vec![
|
components: vec![
|
||||||
Component::Constant("measure".into()),
|
Component::Constant("/measure/".into()),
|
||||||
Component::Parameter("number".into()),
|
Component::Parameter("number".into()),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
"/one/{two}/three",
|
||||||
"/one/{two}/three",
|
"/one/{two}/three",
|
||||||
PathTemplate {
|
PathTemplate {
|
||||||
components: vec![
|
components: vec![
|
||||||
Component::Constant("one".into()),
|
Component::Constant("/one/".into()),
|
||||||
Component::Parameter("two".into()),
|
Component::Parameter("two".into()),
|
||||||
Component::Constant("three".into()),
|
Component::Constant("/three".into()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/{foo}-{bar}-{baz}",
|
||||||
|
"/{foo}-{bar}-{baz}",
|
||||||
|
PathTemplate {
|
||||||
|
components: vec![
|
||||||
|
Component::Constant("/".into()),
|
||||||
|
Component::Parameter("foo".into()),
|
||||||
|
Component::Constant("-".into()),
|
||||||
|
Component::Parameter("bar".into()),
|
||||||
|
Component::Constant("-".into()),
|
||||||
|
Component::Parameter("baz".into()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"//normalise/////{adjacent}:x///slashes",
|
||||||
|
"/normalise/{adjacent}:x/slashes",
|
||||||
|
PathTemplate {
|
||||||
|
components: vec![
|
||||||
|
Component::Constant("/normalise/".into()),
|
||||||
|
Component::Parameter("adjacent".into()),
|
||||||
|
Component::Constant(":x/slashes".into()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/v1/files/{fileId}:completeUpload",
|
||||||
|
"/v1/files/{fileId}:completeUpload",
|
||||||
|
PathTemplate {
|
||||||
|
components: vec![
|
||||||
|
Component::Constant("/v1/files/".into()),
|
||||||
|
Component::Parameter("fileId".into()),
|
||||||
|
Component::Constant(":completeUpload".into()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/v1/folders/{folderId}/sessions/{sessionId}:complete",
|
||||||
|
"/v1/folders/{folderId}/sessions/{sessionId}:complete",
|
||||||
|
PathTemplate {
|
||||||
|
components: vec![
|
||||||
|
Component::Constant("/v1/folders/".into()),
|
||||||
|
Component::Parameter("folderId".into()),
|
||||||
|
Component::Constant("/sessions/".into()),
|
||||||
|
Component::Parameter("sessionId".into()),
|
||||||
|
Component::Constant(":complete".into()),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (path, want) in trials.iter() {
|
for (path, expect_string, want) in trials.iter() {
|
||||||
match parse(path) {
|
match parse(path) {
|
||||||
Ok(t) => assert_eq!(&t, want),
|
Ok(t) => {
|
||||||
|
assert_eq!(&t, want);
|
||||||
|
assert_eq!(t.to_string().as_str(), *expect_string);
|
||||||
|
}
|
||||||
Err(e) => panic!("path {} {}", path, e),
|
Err(e) => panic!("path {} {}", path, e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,4 +288,26 @@ mod test {
|
||||||
};
|
};
|
||||||
assert_eq!(want.to_string(), out.to_string());
|
assert_eq!(want.to_string(), out.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile2() {
|
||||||
|
let mut rename = HashMap::new();
|
||||||
|
let one = "one".to_string();
|
||||||
|
let two = "two".to_string();
|
||||||
|
let three = "three".to_string();
|
||||||
|
rename.insert(&one, &one);
|
||||||
|
rename.insert(&two, &two);
|
||||||
|
rename.insert(&three, &three);
|
||||||
|
let t = parse("/abc/def:{one}:jkl/{two}/a:{three}").unwrap();
|
||||||
|
let out = t.compile(rename, quote::quote! { self });
|
||||||
|
let want = quote::quote! {
|
||||||
|
let url = format!("{}/abc/def:{}:jkl/{}/a:{}",
|
||||||
|
self.baseurl,
|
||||||
|
encode_path(&one.to_string()),
|
||||||
|
encode_path(&two.to_string()),
|
||||||
|
encode_path(&three.to_string()),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
assert_eq!(want.to_string(), out.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,12 +66,12 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
///Sends a `POST` request to ``
|
///Sends a `POST` request to `/`
|
||||||
pub async fn default_params<'a>(
|
pub async fn default_params<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
body: &'a types::BodyWithDefaults,
|
body: &'a types::BodyWithDefaults,
|
||||||
) -> Result<ResponseValue<ByteStream>, Error<ByteStream>> {
|
) -> Result<ResponseValue<ByteStream>, Error<ByteStream>> {
|
||||||
let url = format!("{}", self.baseurl,);
|
let url = format!("{}/", self.baseurl,);
|
||||||
let request = self.client.post(url).json(&body).build()?;
|
let request = self.client.post(url).json(&body).build()?;
|
||||||
let result = self.client.execute(request).await;
|
let result = self.client.execute(request).await;
|
||||||
let response = result?;
|
let response = result?;
|
||||||
|
|
|
@ -47,11 +47,11 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
///Sends a `GET` request to ``
|
///Sends a `GET` request to `/`
|
||||||
pub async fn freeform_response<'a>(
|
pub async fn freeform_response<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
) -> Result<ResponseValue<ByteStream>, Error<ByteStream>> {
|
) -> Result<ResponseValue<ByteStream>, Error<ByteStream>> {
|
||||||
let url = format!("{}", self.baseurl,);
|
let url = format!("{}/", self.baseurl,);
|
||||||
let request = self.client.get(url).build()?;
|
let request = self.client.get(url).build()?;
|
||||||
let result = self.client.execute(request).await;
|
let result = self.client.execute(request).await;
|
||||||
let response = result?;
|
let response = result?;
|
||||||
|
|
Loading…
Reference in New Issue