use futures::{ Future, Stream };
use futures::future::ok;
use tokio_core::reactor::Core;
use hyper::{ Body, Headers, Uri, Method, Error };
use hyper::client::{ Client, Request };
use hyper::header::{ Authorization, Accept, ContentType,
ETag, IfNoneMatch, UserAgent, qitem };
use hyper::mime::Mime;
use hyper::StatusCode;
use hyper_rustls::HttpsConnector;
use serde::Serialize;
use serde_json;
use users;
use misc;
use repos;
use errors::*;
use util::url_join;
use Json;
use std::rc::Rc;
use std::cell::RefCell;
pub struct Github {
token: String,
core: Rc<RefCell<Core>>,
client: Rc<Client<HttpsConnector>>,
}
impl Clone for Github {
fn clone(&self) -> Self {
Self {
token: self.token.clone(),
core: self.core.clone(),
client: self.client.clone(),
}
}
}
new_type!(GetQueryBuilder);
new_type!(PutQueryBuilder);
new_type!(PostQueryBuilder);
new_type!(DeleteQueryBuilder);
new_type!(PatchQueryBuilder);
new_type!(CustomQuery);
new_type!(Executor);
impl Github {
pub fn new<T>(token: T) -> Result<Self>
where T: AsRef<str> {
let core = Core::new().chain_err(|| "Unable to build a new Core")?;
let handle = core.handle();
let client = Client::configure()
.connector(HttpsConnector::new(4,&handle))
.build(&handle);
Ok(Self {
token: token.as_ref().into(),
core: Rc::new(RefCell::new(core)),
client: Rc::new(client),
})
}
pub fn get_token(&self) -> &str {
&self.token
}
pub fn set_token<T>(&mut self, token: T)
where T: AsRef<str> {
self.token = token.as_ref().into();
}
pub fn get_core(&self) -> &Rc<RefCell<Core>> {
&self.core
}
pub fn get(&self) -> GetQueryBuilder {
self.into()
}
pub fn put_empty(&self) -> PutQueryBuilder {
self.into()
}
pub fn put<T>(&self, body: T) -> PutQueryBuilder
where T: Serialize {
let mut qb: PutQueryBuilder = self.into();
if let Ok(mut qbr) = qb.request {
let serialized = serde_json::to_vec(&body);
match serialized {
Ok(json) => {
qbr.get_mut().set_body(json);
qb.request = Ok(qbr);
},
Err(_) => {
qb.request = Err("Unable to serialize data to JSON".into());
}
}
}
qb
}
pub fn post<T>(&self, body: T) -> PostQueryBuilder
where T: Serialize {
let mut qb: PostQueryBuilder = self.into();
if let Ok(mut qbr) = qb.request {
let serialized = serde_json::to_vec(&body);
match serialized {
Ok(json) => {
qbr.get_mut().set_body(json);
qb.request = Ok(qbr);
},
Err(_) => {
qb.request = Err("Unable to serialize data to JSON".into());
}
}
}
qb
}
pub fn patch<T>(&self, body: T) -> PatchQueryBuilder
where T: Serialize {
let mut qb: PatchQueryBuilder = self.into();
if let Ok(mut qbr) = qb.request {
let serialized = serde_json::to_vec(&body);
match serialized {
Ok(json) => {
qbr.get_mut().set_body(json);
qb.request = Ok(qbr);
},
Err(_) => {
qb.request = Err("Unable to serialize data to JSON".into());
}
}
}
qb
}
pub fn delete<T>(&self, body: T) -> DeleteQueryBuilder
where T: Serialize {
let mut qb: DeleteQueryBuilder = self.into();
if let Ok(mut qbr) = qb.request {
let serialized = serde_json::to_vec(&body);
match serialized {
Ok(json) => {
qbr.get_mut().set_body(json);
qb.request = Ok(qbr);
},
Err(_) => {
qb.request = Err("Unable to serialize data to JSON".into());
}
}
}
qb
}
pub fn delete_empty(&self) -> DeleteQueryBuilder {
self.into()
}
}
impl <'g> GetQueryBuilder<'g> {
func_client!(custom_endpoint, CustomQuery, endpoint_str);
func_client!(emojis, misc::get::Emojis<'g>);
func_client!(events, misc::get::Events<'g>);
func_client!(feeds, misc::get::Feeds<'g>);
func_client!(meta, misc::get::Meta<'g>);
func_client!(rate_limit, misc::get::RateLimit<'g>);
func_client!(user, users::get::User<'g>);
func_client!(users, users::get::Users<'g>);
func_client!(repos, repos::get::Repos<'g>);
pub fn set_etag(mut self, tag: ETag) -> Self {
match self.request {
Ok(mut req) => {
let ETag(tag) = tag;
req.get_mut().headers_mut().set(IfNoneMatch::Items(vec![tag]));
self.request = Ok(req);
self
}
Err(_) => self,
}
}
}
impl <'g> PutQueryBuilder<'g> {
func_client!(custom_endpoint, CustomQuery, endpoint_str);
func_client!(user, users::put::User<'g>);
pub fn set_etag(mut self, tag: ETag) -> Self {
match self.request {
Ok(mut req) => {
let ETag(tag) = tag;
req.get_mut().headers_mut().set(IfNoneMatch::Items(vec![tag]));
self.request = Ok(req);
self
}
Err(_) => self,
}
}
}
impl <'g> DeleteQueryBuilder<'g> {
func_client!(custom_endpoint, CustomQuery, endpoint_str);
func_client!(user, users::delete::User<'g>);
pub fn set_etag(mut self, tag: ETag) -> Self {
match self.request {
Ok(mut req) => {
let ETag(tag) = tag;
req.get_mut().headers_mut().set(IfNoneMatch::Items(vec![tag]));
self.request = Ok(req);
self
}
Err(_) => self,
}
}
}
impl <'g> PostQueryBuilder<'g> {
func_client!(custom_endpoint, CustomQuery, endpoint_str);
func_client!(user, users::post::User<'g>);
func_client!(repos, repos::post::Repos<'g>);
pub fn set_etag(mut self, tag: ETag) -> Self {
match self.request {
Ok(mut req) => {
let ETag(tag) = tag;
req.get_mut().headers_mut().set(IfNoneMatch::Items(vec![tag]));
self.request = Ok(req);
self
},
Err(_) => self,
}
}
}
impl <'g> PatchQueryBuilder<'g> {
func_client!(custom_endpoint, CustomQuery, endpoint_str);
func_client!(user, users::patch::User<'g>);
pub fn set_etag(mut self, tag: ETag) -> Self {
match self.request {
Ok(mut req) => {
let ETag(tag) = tag;
req.get_mut().headers_mut().set(IfNoneMatch::Items(vec![tag]));
self.request = Ok(req);
self
}
Err(_) => self,
}
}
}
exec!(CustomQuery);
impl <'g> Executor<'g> {
pub fn execute(self) -> Result<(Headers, StatusCode, Option<Json>)> {
let mut core_ref = self.core
.try_borrow_mut()
.chain_err(|| "Unable to get mutable borrow\
to the event loop")?;
let client = self.client;
let work = client
.request(self.request?.into_inner())
.and_then(|res| {
let header = res.headers().clone();
let status = res.status();
res.body().fold(Vec::new(), |mut v, chunk| {
v.extend(&chunk[..]);
ok::<_, Error>(v)
}).map(move |chunks| {
if chunks.is_empty() {
(header, status, None)
} else {
(
header,
status,
Some(serde_json::from_slice(&chunks).unwrap())
)
}
})
});
core_ref.run(work).chain_err(|| "Failed to execute request")
}
}
from!(
@GetQueryBuilder
=> Method::Get
@PutQueryBuilder
=> Method::Put
@PostQueryBuilder
=> Method::Post
@PatchQueryBuilder
=> Method::Patch
@DeleteQueryBuilder
=> Method::Delete
);
from!(
@GetQueryBuilder
=> CustomQuery
@PutQueryBuilder
=> CustomQuery
@PostQueryBuilder
=> CustomQuery
@PatchQueryBuilder
=> CustomQuery
@DeleteQueryBuilder
=> CustomQuery
@CustomQuery
=> Executor
);