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 Sep 28, 2016

Note that this isn't a really realistic example, since the IndexRequest won't have any alternative parameters; everything is mandatory.

@KodrAus
Copy link
Author

KodrAus commented Sep 28, 2016

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.

@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