<commit_before>//! This module contains the code representing Mastodon nodes and API Clients
//!
use errors::*;
use futures::{Future, Stream};
use hyper::Client as WebClient;
use hyper::{Body, Uri, Method};
use hyper::Method::{Get, Patch, Post};
use hyper::client::Request;
use hyper::header::{Headers, ContentType, Authorization, Bearer};
use hyper_tls::HttpsConnector;
use mastodon::ApiHandler;
use serde_json;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use super::entities;
use super::methods;
use super::methods::{
    AccountID,
    APIEndpoint,
    APIEndpointRequest,
    HomeTimelineQuery,
    NotificationID,
    RelationshipsQuery,
    SearchAccountsQuery,
    SearchContentsQuery,
    StatusID,
    TagID,
    TimelineQuery,
    UserFormData
};
use tokio_core::reactor::Core;
use url::Url;


/// The API Client, currently works for version 1 of the Mastodon API.
pub struct Client {
    pub url_base: Url,
    pub token: Bearer
}

impl ApiHandler for Client {
    fn endpoint_url(&self, path: &str) -> Result<Url> {
        self.url_base.clone().join(path).chain_err(|| "could not join path with URL")
    }
    fn endpoint_url_string(&self, path: &str) -> String {
        self.endpoint_url(path).unwrap().into_string()
    }
}

impl APIEndpointRequest for Client {
    fn bearer_token_request(&self, method: Method, uri: Uri) -> Result<Request<Body>> {
        let mut req = Request::new(method, uri);
        req.headers_mut().set(Authorization(self.token.clone()));
        Ok(req)
    }
    fn build_request(&self, endpoint: APIEndpoint) -> Result<Request<Body>> {
        match endpoint {
            APIEndpoint::FetchAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::GetCurrentUser => {
                let url = self.endpoint_url("/api/v1/accounts/verify_credentials")?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::UpdateCurrentUser(d) => {
                let url = self.endpoint_url("/api/v1/accounts/update_credentials")?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Patch, uri)
            },
            APIEndpoint::GetAccountFollowers(a) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/followers", a.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::GetFollowing(a) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/following", a.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::GetAccountStatuses(a) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/statuses", a.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::FollowAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/follow", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::UnfollowAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/unfollow", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::BlockAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/block", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                Ok(Request::new(Post, uri))
            },
            APIEndpoint::UnblockAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/unblock", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                Ok(Request::new(Post, uri))
            },
            APIEndpoint::MuteAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/mute", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::UnmuteAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/unmute", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::GetAccountRelationships(query) => {
                let mut url = self.endpoint_url("/api/v1/accounts/relationships").unwrap();
                match query {
                    RelationshipsQuery::SingleAccount(account) => {
                        url.query_pairs_mut().append_pair("id", &format!("{}", account.id));
                    },
                    RelationshipsQuery::MultipleAccounts(accounts) => {
                        for account in &accounts {
                            url.query_pairs_mut()
                                .append_pair("id[]", &format!("{}", account.id));
                        }
                    }
                };
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::SearchAccounts(q) => {
                let mut url = self.endpoint_url("/api/v1/accounts/search").unwrap();
                url.query_pairs_mut().append_pair("q", &q.q);
                if let Some(limit) = q.limit {
                    url.query_pairs_mut().append_pair("limit", &format!("{}", limit));
                };
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            }
        }
    }

    fn send(&self, endpoint: APIEndpoint, dst: Arc<Mutex<Vec<u8>>>) -> Result<()> {
        let mut core = Core::new().chain_err(|| "Could not start client reactor")?;
        let client = WebClient::configure()
            .connector(HttpsConnector::new(4, &core.handle()))
            .build(&core.handle());

        let req: Request<Body> = self.build_request(endpoint).chain_err(|| "Could not build request")?;
        //req.headers_mut().set(ContentType::form_url_encoded());
        //req.set_body(Body::from(self.form_encode()));

        let mut dst = dst.lock().unwrap();
        let work = client.request(req)
            .and_then(|res| {
                res.body().for_each(|chunk| {
                    dst.extend_from_slice(&chunk);
                    Ok(())
                })
            });
        core.run(work).chain_err(|| "Failed to run registration")?;
        Ok(())
    }
}

impl methods::Accounts for Client {
    fn fetch_account(&self, account_id: AccountID) -> Result<entities::Account> {
        let api_method = APIMethodRequest {
            uri: Uri::from_str(&format!("/api/v1/accounts/{}", account_id.id)).unwrap(),
            ..APIMethodRequest::default()
        };
        println!("FETCH ACCOUNT: {:?}", api_method);
        Ok(entities::Account::default())
    }
    fn get_current_user(&self) -> Result<entities::Account> {
        unimplemented!();
    }
    fn update_current_user(&self, form_data: UserFormData) -> Result<entities::Account> {
        unimplemented!()
    }
    fn get_account_followers(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn get_account_following(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn get_account_statutes(&self, account_id: AccountID) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
    fn follow_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        unimplemented!()
    }
    fn unfollow_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        unimplemented!()
    }
    fn block_account(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }<commit_msg>implement a few of the Accounts trait methods<commit_after>//! This module contains the code representing Mastodon nodes and API Clients
//!
use errors::*;
use futures::{Future, Stream};
use hyper::Client as WebClient;
use hyper::{Body, Uri, Method};
use hyper::Method::{Get, Patch, Post};
use hyper::client::Request;
use hyper::header::{Headers, ContentType, Authorization, Bearer};
use hyper_tls::HttpsConnector;
use mastodon::ApiHandler;
use serde_json;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use super::entities;
use super::methods;
use super::methods::{
    AccountID,
    APIEndpoint,
    APIEndpointRequest,
    HomeTimelineQuery,
    NotificationID,
    RelationshipsQuery,
    SearchAccountsQuery,
    SearchContentsQuery,
    StatusID,
    TagID,
    TimelineQuery,
    UserFormData
};
use tokio_core::reactor::Core;
use url::Url;


/// The API Client, currently works for version 1 of the Mastodon API.
pub struct Client {
    pub url_base: Url,
    pub token: Bearer
}

impl ApiHandler for Client {
    fn endpoint_url(&self, path: &str) -> Result<Url> {
        self.url_base.clone().join(path).chain_err(|| "could not join path with URL")
    }
    fn endpoint_url_string(&self, path: &str) -> String {
        self.endpoint_url(path).unwrap().into_string()
    }
}

impl APIEndpointRequest for Client {
    fn bearer_token_request(&self, method: Method, uri: Uri) -> Result<Request<Body>> {
        let mut req = Request::new(method, uri);
        req.headers_mut().set(Authorization(self.token.clone()));
        Ok(req)
    }
    fn build_request(&self, endpoint: APIEndpoint) -> Result<Request<Body>> {
        match endpoint {
            APIEndpoint::FetchAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::GetCurrentUser => {
                let url = self.endpoint_url("/api/v1/accounts/verify_credentials")?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::UpdateCurrentUser(d) => {
                let url = self.endpoint_url("/api/v1/accounts/update_credentials")?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Patch, uri)
            },
            APIEndpoint::GetAccountFollowers(a) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/followers", a.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::GetFollowing(a) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/following", a.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::GetAccountStatuses(a) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/statuses", a.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::FollowAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/follow", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::UnfollowAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/unfollow", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::BlockAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/block", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                Ok(Request::new(Post, uri))
            },
            APIEndpoint::UnblockAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/unblock", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                Ok(Request::new(Post, uri))
            },
            APIEndpoint::MuteAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/mute", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::UnmuteAccount(account) => {
                let url = self.endpoint_url(&format!("/api/v1/accounts/{}/unmute", account.id))?;
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Post, uri)
            },
            APIEndpoint::GetAccountRelationships(query) => {
                let mut url = self.endpoint_url("/api/v1/accounts/relationships").unwrap();
                match query {
                    RelationshipsQuery::SingleAccount(account) => {
                        url.query_pairs_mut().append_pair("id", &format!("{}", account.id));
                    },
                    RelationshipsQuery::MultipleAccounts(accounts) => {
                        for account in &accounts {
                            url.query_pairs_mut()
                                .append_pair("id[]", &format!("{}", account.id));
                        }
                    }
                };
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            },
            APIEndpoint::SearchAccounts(q) => {
                let mut url = self.endpoint_url("/api/v1/accounts/search").unwrap();
                url.query_pairs_mut().append_pair("q", &q.q);
                if let Some(limit) = q.limit {
                    url.query_pairs_mut().append_pair("limit", &format!("{}", limit));
                };
                let uri = Uri::from_str(url.as_str()).unwrap();
                self.bearer_token_request(Get, uri)
            }
        }
    }

    fn send(&self, endpoint: APIEndpoint, dst: Arc<Mutex<Vec<u8>>>) -> Result<()> {
        let mut core = Core::new().chain_err(|| "Could not start client reactor")?;
        let client = WebClient::configure()
            .connector(HttpsConnector::new(4, &core.handle()))
            .build(&core.handle());

        let req: Request<Body> = self.build_request(endpoint).chain_err(|| "Could not build request")?;
        //req.headers_mut().set(ContentType::form_url_encoded());
        //req.set_body(Body::from(self.form_encode()));

        let mut dst = dst.lock().unwrap();
        let work = client.request(req)
            .and_then(|res| {
                res.body().for_each(|chunk| {
                    dst.extend_from_slice(&chunk);
                    Ok(())
                })
            });
        core.run(work).chain_err(|| "Failed to run registration")?;
        Ok(())
    }
}

impl methods::Accounts for Client {
    fn fetch_account(&self, account_id: AccountID) -> Result<entities::Account> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::FetchAccount(account_id);
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error fetching account.")
    }
    fn get_current_user(&self) -> Result<entities::Account> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::GetCurrentUser;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error getting current user.")
    }
    fn update_current_user(&self, form_data: UserFormData) -> Result<entities::Account> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::UpdateCurrentUser(form_data);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error updating current user.")
    }
    fn get_account_followers(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::GetAccountFollowers(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error getting account followers.")
    }
    fn get_account_following(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::GetFollowing(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error getting account's following.")
    }
    fn get_account_statutes(&self, account_id: AccountID) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
    fn follow_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        unimplemented!()
    }
    fn unfollow_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        unimplemented!()
    }
    fn block_account(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
{'fraction_non_alphanumeric': 0.1528830313014827, 'fraction_numerical': 0.0019220208676551346, 'mean_word_length': 2.308684593023256, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 109, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7180471', 'n_tokens_mistral': 5388, 'n_tokens_neox': 5299, 'n_words': 909}