Implement substitution of variables in endpoint paths

This commit is contained in:
Jonas Platte 2017-06-30 01:29:23 +10:00
parent 5180297d81
commit 62971e63cd
3 changed files with 99 additions and 5 deletions

View File

@ -22,7 +22,6 @@ serde = "1.0.8"
serde_derive = "1.0.8"
serde_json = "1.0.2"
serde_urlencoded = "0.5.1"
url = "1.5.1"
[lib]
doctest = false

View File

@ -36,6 +36,61 @@ impl ToTokens for Api {
tokens
};
let set_request_path = if self.request.has_path_fields() {
let path_str_quoted = path.as_str();
assert!(
path_str_quoted.starts_with('"') && path_str_quoted.ends_with('"'),
"path needs to be a string literal"
);
let path_str = &path_str_quoted[1 .. path_str_quoted.len() - 1];
assert!(path_str.starts_with('/'), "path needs to start with '/'");
assert!(
path_str.chars().filter(|c| *c == ':').count() == self.request.path_field_count(),
"number of declared path parameters needs to match amount of placeholders in path"
);
let request_path_init_fields = self.request.request_path_init_fields();
let mut tokens = quote! {
let request_path = RequestPath {
#request_path_init_fields
};
// This `unwrap()` can only fail when the url is a
// cannot-be-base url like `mailto:` or `data:`, which is not
// the case for our placeholder url.
let mut path_segments = url.path_segments_mut().unwrap();
};
for segment in path_str[1..].split('/') {
tokens.append(quote! {
path_segments.push
});
tokens.append("(");
if segment.starts_with(':') {
tokens.append("&request_path.");
tokens.append(&segment[1..]);
tokens.append(".to_string()");
} else {
tokens.append("\"");
tokens.append(segment);
tokens.append("\"");
}
tokens.append(");");
}
tokens
} else {
quote! {
url.set_path(metadata.path);
}
};
let set_request_query = if self.request.has_query_fields() {
let request_query_init_fields = self.request.request_query_init_fields();
@ -159,8 +214,9 @@ impl ToTokens for Api {
// the calling code. Previously (with hyper::Uri) this was
// not required, but Url::parse only accepts absolute urls.
let mut url = ::url::Url::parse("http://invalid-host-please-change/").unwrap();
url.set_path(metadata.path);
#set_request_query
{ #set_request_path }
{ #set_request_query }
let mut hyper_request = ::hyper::Request::new(
metadata.method,
@ -168,7 +224,7 @@ impl ToTokens for Api {
url.into_string().parse().unwrap(),
);
#add_body_to_request
{ #add_body_to_request }
Ok(hyper_request)
}

View File

@ -11,10 +11,18 @@ impl Request {
self.fields.iter().any(|field| field.is_body())
}
pub fn has_path_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_path())
}
pub fn has_query_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_query())
}
pub fn path_field_count(&self) -> usize {
self.fields.iter().filter(|field| field.is_path()).count()
}
pub fn newtype_body_field(&self) -> Option<&Field> {
for request_field in self.fields.iter() {
match *request_field {
@ -32,6 +40,10 @@ impl Request {
self.struct_init_fields(RequestFieldKind::Body)
}
pub fn request_path_init_fields(&self) -> Tokens {
self.struct_init_fields(RequestFieldKind::Path)
}
pub fn request_query_init_fields(&self) -> Tokens {
self.struct_init_fields(RequestFieldKind::Query)
}
@ -178,9 +190,32 @@ impl ToTokens for Request {
tokens.append("}");
}
if self.has_path_fields() {
tokens.append(quote! {
/// Data in the request path.
#[derive(Debug)]
struct RequestPath
});
tokens.append("{");
for request_field in self.fields.iter() {
match *request_field {
RequestField::Path(ref field) => {
field.to_tokens(&mut tokens);
tokens.append(",");
}
_ => {}
}
}
tokens.append("}");
}
if self.has_query_fields() {
tokens.append(quote! {
/// Data in the request url's query parameters
/// Data in the request url's query parameters.
#[derive(Debug, Serialize)]
struct RequestQuery
});
@ -237,6 +272,10 @@ impl RequestField {
self.kind() == RequestFieldKind::Body
}
fn is_path(&self) -> bool {
self.kind() == RequestFieldKind::Path
}
fn is_query(&self) -> bool {
self.kind() == RequestFieldKind::Query
}