Skip to content

Instantly share code, notes, and snippets.

@KodrAus
Last active November 22, 2016 11:11
Show Gist options
  • Save KodrAus/6ad3bfc3286ed5e1493c73060dc6dbab to your computer and use it in GitHub Desktop.
Save KodrAus/6ad3bfc3286ed5e1493c73060dc6dbab to your computer and use it in GitHub Desktop.
elastic_requests API
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![]);
}
@KodrAus
Copy link
Author

KodrAus commented Nov 22, 2016

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