Last active
November 22, 2016 11:11
-
-
Save KodrAus/6ad3bfc3286ed5e1493c73060dc6dbab to your computer and use it in GitHub Desktop.
elastic_requests API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::borrow::Cow; | |
// Our wrapper types are basically a thin layer over a &str | |
macro_rules! wrapper { | |
($name:ident) => { | |
pub struct $name<'a>(pub Cow<'a, str>); | |
impl <'a> From<&'a str> for $name<'a> { | |
fn from(value: &'a str) -> $name<'a> { | |
$name(Cow::Borrowed(value)) | |
} | |
} | |
impl <'a> From<String> for $name<'a> { | |
fn from(value: String) -> $name<'a> { | |
$name(Cow::Owned(value)) | |
} | |
} | |
impl <'a> ::std::ops::Deref for $name<'a> { | |
type Target = str; | |
fn deref(&self) -> &str { | |
&self.0 | |
} | |
} | |
} | |
} | |
wrapper!(Index); | |
wrapper!(Type); | |
wrapper!(Id); | |
// We also declare a more complex Indices type | |
pub struct Indices<'a>(Cow<'a, str>); | |
impl <'a> AsRef<str> for Indices<'a> { | |
fn as_ref(&self) -> &str { | |
&self.0 | |
} | |
} | |
impl <'a, T> From<T> for Indices<'a> where | |
T: Into<Index<'a>> { | |
fn from(value: T) -> Indices<'a> { | |
Indices(value.into().0) | |
} | |
} | |
impl <'a, T> From<Vec<T>> for Indices<'a> where | |
T: Into<Index<'a>> { | |
fn from(value: Vec<T>) -> Indices<'a> { | |
let indices: Vec<Cow<'a, str>> = value | |
.into_iter() | |
.map(|i| i.into().0) | |
.collect(); | |
let indices = match indices.len() { | |
0 => Cow::Borrowed(""), | |
1 => { | |
let mut indices = indices.into_iter(); | |
indices.next().unwrap() | |
}, | |
_ => Cow::Owned(indices.join(",")) | |
}; | |
Indices(indices) | |
} | |
} | |
// --- START CODEGEN --- // | |
// An example of some request parameters. Most of them are simpler than this | |
pub struct RequestParams<'a> { | |
_url: RequestUrlParams<'a>, | |
_body: Vec<u8> | |
} | |
pub enum RequestUrlParams<'a> { | |
None, | |
Indices(Indices<'a>), | |
Index(Index<'a>), | |
Type(Type<'a>), | |
IndexTypeId(Index<'a>, Type<'a>, Id<'a>) | |
} | |
// Impl Into for () when there are no params | |
impl <'a> Into<RequestUrlParams<'a>> for () { | |
fn into(self) -> RequestUrlParams<'a> { | |
RequestUrlParams::None | |
} | |
} | |
// Impl Into for T when there is 1 param | |
impl <'a> Into<RequestUrlParams<'a>> for Index<'a> { | |
fn into(self) -> RequestUrlParams<'a> { | |
RequestUrlParams::Index(self) | |
} | |
} | |
impl <'a> Into<RequestUrlParams<'a>> for Type<'a> { | |
fn into(self) -> RequestUrlParams<'a> { | |
RequestUrlParams::Type(self) | |
} | |
} | |
impl <'a> Into<RequestUrlParams<'a>> for Indices<'a> { | |
fn into(self) -> RequestUrlParams<'a> { | |
RequestUrlParams::Indices(self) | |
} | |
} | |
// Impl Into for (T1, ... , Tn) when there are n params | |
impl <'a> Into<RequestUrlParams<'a>> for (Index<'a>, Type<'a>, Id<'a>) { | |
fn into(self) -> RequestUrlParams<'a> { | |
RequestUrlParams::IndexTypeId(self.0, self.1, self.2) | |
} | |
} | |
// Impl From (I: Into<RequestUrlParams>, Body) for RequestParams | |
impl <'a, I> From<(I, Vec<u8>)> for RequestParams<'a> where | |
I: Into<RequestUrlParams<'a>> { | |
fn from(value: (I, Vec<u8>)) -> RequestParams<'a> { | |
RequestParams { | |
_url: value.0.into(), | |
_body: value.1 | |
} | |
} | |
} | |
mod index_request { | |
use super::*; | |
// An example request method, that accepts anything that binds to the params type | |
pub fn request<'a, I>(_params: I) where I: Into<RequestParams<'a>> { | |
} | |
// We could also generate functions that take parameters in a more standard format at module level | |
// This would be the recommended way of using the API if you're not supplying a compatible type | |
pub fn index<'a, I>(index: I, body: Vec<u8>) where I: Into<Index<'a>> { | |
request((index.into(), body)); | |
} | |
pub fn index_type_id<'a, I1, I2, I3>(index: I1, ty: I2, id: I3, body: Vec<u8>) where | |
I1: Into<Index<'a>>, | |
I2: Into<Type<'a>>, | |
I3: Into<Id<'a>> { | |
request(((index.into(), ty.into(), id.into()), body)); | |
} | |
} | |
// --- END CODEGEN --- // | |
struct MyTypeToIndex { | |
pub id: i32 | |
} | |
impl <'a, 'b> Into<RequestParams<'a>> for &'b MyTypeToIndex { | |
fn into(self) -> RequestParams<'a> { | |
((Index::from("index"), Type::from("my_type_to_index"), Id::from(self.id.to_string())), vec![]).into() | |
} | |
} | |
fn main() { | |
// Calls taking input params that you probably don't really want | |
index_request::request((Index::from("index"), vec![])); | |
index_request::request(((Index::from("index"), Type::from("type"), Id::from("1")), vec![])); | |
index_request::request((Indices::from(Index::from("index")), vec![])); | |
index_request::request((Indices::from("index"), vec![])); | |
index_request::request(( | |
Indices::from(vec![ | |
"index", | |
"another_index" | |
]), | |
vec![] | |
)); | |
// Calls taking more sane input items | |
let my_type = MyTypeToIndex { id: 1 }; | |
index_request::request(&my_type); | |
index_request::index("index", vec![]); | |
index_request::index_type_id("index", "type", "id", vec![]); | |
} |
Also, since we're exposing methods for the individual parameters in UrlParams
we probably don't need to bother with tuple conversions.
I'm still ok with generating Index
, Type
types etc though.
Definitely not going to do the tuple conversions. iron
does this and I think it makes method signstures hard to grok, especially for new users.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note that this isn't a really realistic example, since the
IndexRequest
won't have any alternative parameters; everything is mandatory.