github-gql-rs 0.0.1

Pure Rust bindings to the Github V4 API using GraphQL
Documentation
// Tokio/Future Imports
use tokio_core::reactor::Core;
use futures::future::ok;
use futures::{ Stream, Future };

use serde_json;
// Hyper Imports
use hyper::{ self, Headers };
use hyper::client::Client;
use hyper::StatusCode;
use hyper_rustls::HttpsConnector;

// Serde Imports
use serde::de::DeserializeOwned;

// Lib Imports
use errors::*;
use query::Query;
use mutation::Mutation;
use IntoGithubRequest;

// Std Imports
use std::rc::Rc;
use std::cell::RefCell;

/// Struct used to make calls to the Github API.
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(),
        }
    }
}

impl Github {
    /// Create a new Github client struct. It takes a type that can convert into
    /// a `String` (`&str` or `Vec<u8>` for example). As long as the function is
    /// given a valid API Token your requests will work.
    pub fn new<T>(token: T) -> Result<Self>
        where T: ToString
    {
        let core = Core::new()?;
        let handle = core.handle();
        let client = Client::configure()
            .connector(HttpsConnector::new(4,&handle))
            .build(&handle);
        Ok(Self {
            token: token.to_string(),
            core: Rc::new(RefCell::new(core)),
            client: Rc::new(client),
        })
    }

    /// Get the currently set Authorization Token
    pub fn get_token(&self) -> &str {
        &self.token
    }

    /// Change the currently set Authorization Token using a type that can turn
    /// into an &str. Must be a valid API Token for requests to work.
    pub fn set_token<T>(&mut self, token: T)
        where T: ToString {
        self.token = token.to_string();
    }

    /// Exposes the inner event loop for those who need
    /// access to it. The recommended way to safely access
    /// the core would be
    ///
    /// ```text
    /// let g = Github::new("API KEY");
    /// let core = g.get_core();
    /// // Handle the error here.
    /// let ref mut core_mut = *core.try_borrow_mut()?;
    /// // Do stuff with the core here. This prevents a runtime failure by
    /// // having two mutable borrows to the core at the same time.
    /// ```
    ///
    /// This is how other parts of the API are implemented to avoid causing your
    /// program to crash unexpectedly. While you could borrow without the
    /// `Result` being handled it's highly recommended you don't unless you know
    /// there is no other mutable reference to it.
    pub fn get_core(&self) -> &Rc<RefCell<Core>> {
        &self.core
    }

    pub fn query<T>(
        &mut self,
        query: &Query) -> Result<(Headers, StatusCode, Option<T>)>
            where T:DeserializeOwned
    {
        self.run(query)
    }

    pub fn mutation<T>(&mut self, mutation: &Mutation)
        -> Result<(Headers, StatusCode, Option<T>)>
        where T:DeserializeOwned
    {
        self.run(mutation)
    }

    fn run<T,I>(&mut self, request: &I) -> Result<(Headers, StatusCode, Option<T>)>
            where T: DeserializeOwned,
                  I: IntoGithubRequest,
    {
        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(request.into_github_req(&self.token)?)
            .and_then(|res| {
                let header = res.headers().clone();
                let status = res.status();
                res.body().fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    ok::<_, hyper::Error>(v)
                }).map(move |chunks| {
                    if chunks.is_empty() {
                        Ok((header, status, None))
                    } else {
                        Ok((
                            header,
                            status,
                            Some(serde_json::from_slice(&chunks)
                                    .chain_err(|| "Failed to parse response body")?)
                        ))
                    }
                })
            });
        core_ref.run(work).chain_err(|| "Failed to execute request")?
    }
}