<commit_before>use std::any::{ Any, AnyMutRefExt };
use std::boxed::BoxAny;
use typedef::TypeDef;
use factory::{ Factory, Getter };

/// Proxy for configuring factory list without caring about the type used.
///
/// ```
/// # extern crate metafactory;
/// use std::any::Any;
/// use metafactory::{ metafactory, argless_as_factory, AsFactoryExt };
/// use metafactory::aggregate::Aggregate;
///
/// fn main() {
///     // Let's say we know that we will have bunch of `bool` factories.
///     // In that case we create a new aggregate for them:
///     let mut aggregate = Aggregate::new::<bool>();
///
///     // Once we actually have our factories, we can inject them into aggregate
///     // without dealing with types. Of course, we should make sure that types
///     // actually match before doing that in the code that is using this
///     // implementation.
///     aggregate.push_items(vec![
///         argless_as_factory(|| true),
///         argless_as_factory(true),
///         argless_as_factory(|| 4i == 8),
///     ]);
///
///     // Once we are ready to use the aggregate as factory, we can call `new_factory` to
///     // convert all dynamic stuff to statically constructed call hierarchy:
///     let anyed_bool_array_factory = aggregate.new_factory();
///
///     // Of course, that returns it anyed (`Box<Any>`), but we can easily get un-anyed version
///     // by downcasting to `Factory<Vec<bool>>` or using a convenience extension
///     // method for that:
///     let bool_array_factory = anyed_bool_array_factory
///         .as_factory_of::<Vec<bool>>().unwrap();
///
///     // Calling it should produce expected boolean vector:
///     assert_eq!(bool_array_factory.take(), vec![true, true, false]);
///
///     // Of course, the aggregate itself should be usable as argument
///     // for other factories:
///     let metafactory_all_true = metafactory(|values: Vec<bool>| {
///         values.iter().fold(true, |a, &i| a & i)
///     });
///
///     // We can pass it when constructing a factory for this lambda metafactory:
///     let factory_all_true = metafactory_all_true.new(vec![
///         box bool_array_factory.clone() as Box<Any>
///     ])
///         .ok().unwrap() // check for errors here
///         .as_factory_of::<bool>().unwrap() // same story with downcasting
///     ;
///
///     assert_eq!(factory_all_true.take(), false); // not all values are true
/// }
/// ```
pub struct Aggregate<'a> {
    typedef: TypeDef,
    any_getter: Box<Any>,
    do_push_items: |&mut Box<Any>, Vec<Box<Any>>|:'a -> (),
    do_new_factory: |&mut Box<Any>|:'a -> Box<Any>, // Don't worry, it's like Javascript ;)
}

impl<'a> Aggregate<'a> {
    /// Create new aggregate instance for specified type.
    pub fn new<T: 'static>() -> Aggregate<'a> {
        Aggregate {
            typedef: TypeDef::of::<T>(),
            any_getter: box AggregateGetter::<T>::new(),
            do_push_items: |any_getter, items| {
                let getter: &mut AggregateGetter<T> = any_getter
                    .downcast_mut::<AggregateGetter<T>>().unwrap();

                let len = items.len();
                let items_iter = items.into_iter()
                    .map(|i| *i.downcast::<Factory<T>>().ok().unwrap());

                getter.factories.reserve_exact(len);
                getter.factories.extend(items_iter);
            },
            do_new_factory: |any_getter| {
                let getter: &mut AggregateGetter<T> = any_getter
                    .downcast_mut::<AggregateGetter<T>>().unwrap();

                box Factory::<Vec<T>>::new(getter.boxed_clone())
            }
        }
    }

    /// Return aggregated type.
    pub fn get_arg_type(&self) -> TypeDef {
        self.typedef.clone()
    }

    /// Push factory items into aggregate.
    ///
    /// Note that all items should already match contained aggregate type:
    /// if aggregate was created for `int`, all pushed factories should
    /// produce int. Otherwise this method will panic your app.
    pub fn push_items(&mut self, items: Vec<Box<Any>>) {
        (self.do_push_items)(&mut self.any_getter, items);
    }

    /// Produces factory usable as argument for other factories.
    ///
    /// If inner factories make `int` values, this method will make factory
    /// that makes `Vec<int>` values.
    pub fn new_factory(&mut self) -> Box<Any> {
        (self.do_new_factory)(&mut self.any_getter)
    }

    /// Returns type which is used as aggregate result.
    pub fn container_of<T: 'static>() -> TypeDef {
        TypeDef::of::<Vec<T>>()
    }
}

struct AggregateGetter<T: 'static> {
    factories: Vec<Factory<T>>,
}

impl<T> Clone for AggregateGetter<T> {
    fn clone(&self) -> AggregateGetter<T> {
        AggregateGetter::<T> {
            factories: self.factories.clone()
        }
    }
}

impl<T> AggregateGetter<T> {
    pub fn new() -> AggregateGetter<T> {
        AggregateGetter::<T> {
            factories: Vec::with_capacity(0)
        }
    }
}

impl<T> Getter<Vec<T>> for AggregateGetter<T> {
    fn take(&self) -> Vec<T> {

        // Reserve exact result size.
        let mut items = Vec::<T>::with_capacity(self.factories.len());

        // Construct results from factory results.
        items.extend(
            self.factories.iter()
                .map(|f| f.take())
        );

        items
    }

    fn boxed_clone(&self) -> Box<Getter<Vec<T>> + 'static> {
        box self.clone()
    }
}

#[cfg(test)]
mod test {
    use { argless_as_factory, metafactory, AsFactoryExt };
    use super::{ Aggregate };

    #[test]
    fn should_be_usable_as_vec_of_types() {
        let mut container = Aggregate::new::<int>();
        container.push_items(vec![
            argless_as_factory(5i),
            argless_as_factory(13i)
        ]);

        let parent_metafactory = metafactory(
            |items: Vec<int>|
                items.into_iter()
                    .map(|i| format!("{}", i))
                    .collect::<Vec<String>>()
                    .connect(", ")
        );

        let parent_getter = parent_metafactory
            .new(vec![
                container.new_factory()
            ]).ok().unwrap()
            .as_factory_of::<String>().unwrap()
        ;

        assert_eq!(parent_getter.take(), "5, 13");
    }
}
<commit_msg>Improve doc.<commit_after>//! Implements a factory that aggregates the results of other factories of
//! the same type.

use std::any::{ Any, AnyMutRefExt };
use std::boxed::BoxAny;
use typedef::TypeDef;
use factory::{ Factory, Getter };

/// Proxy for initializing aggregate factory without caring about the type used.
///
/// ```
/// # extern crate metafactory;
/// use std::any::Any;
/// use metafactory::{ metafactory, argless_as_factory, AsFactoryExt };
/// use metafactory::aggregate::Aggregate;
///
/// fn main() {
///     // Let's say we know that we will have bunch of `bool` factories.
///     // In that case we create a new aggregate for them:
///     let mut aggregate = Aggregate::new::<bool>();
///
///     // Once we actually have our factories, we can inject them into aggregate
///     // without dealing with types. Of course, we should make sure that types
///     // actually match before doing that in the code that is using this
///     // implementation.
///     aggregate.push_items(vec![
///         argless_as_factory(|| true),
///         argless_as_factory(true),
///         argless_as_factory(|| 4i == 8),
///     ]);
///
///     // Once we are ready to use the aggregate as factory, we can call `new_factory` to
///     // convert all dynamic stuff to statically constructed call hierarchy:
///     let anyed_bool_array_factory = aggregate.new_factory();
///
///     // Of course, that returns it anyed (`Box<Any>`), but we can easily get un-anyed version
///     // by downcasting to `Factory<Vec<bool>>` or using a convenience extension
///     // method for that:
///     let bool_array_factory = anyed_bool_array_factory
///         .as_factory_of::<Vec<bool>>().unwrap();
///
///     // Calling it should produce expected boolean vector:
///     assert_eq!(bool_array_factory.take(), vec![true, true, false]);
///
///     // Of course, the aggregate itself should be usable as argument
///     // for other factories:
///     let metafactory_all_true = metafactory(|values: Vec<bool>| {
///         values.iter().fold(true, |a, &i| a & i)
///     });
///
///     // We can pass it when constructing a factory for this lambda metafactory:
///     let factory_all_true = metafactory_all_true.new(vec![
///         box bool_array_factory.clone() as Box<Any>
///     ])
///         .ok().unwrap() // check for errors here
///         .as_factory_of::<bool>().unwrap() // same story with downcasting
///     ;
///
///     assert_eq!(factory_all_true.take(), false); // not all values are true
/// }
/// ```
pub struct Aggregate<'a> {
    typedef: TypeDef,
    any_getter: Box<Any>,
    do_push_items: |&mut Box<Any>, Vec<Box<Any>>|:'a -> (),
    do_new_factory: |&mut Box<Any>|:'a -> Box<Any>, // Don't worry, it's like Javascript ;)
}

impl<'a> Aggregate<'a> {
    /// Create new aggregate instance for specified type.
    pub fn new<T: 'static>() -> Aggregate<'a> {
        Aggregate {
            typedef: TypeDef::of::<T>(),
            any_getter: box AggregateGetter::<T>::new(),
            do_push_items: |any_getter, items| {
                let getter: &mut AggregateGetter<T> = any_getter
                    .downcast_mut::<AggregateGetter<T>>().unwrap();

                let len = items.len();
                let items_iter = items.into_iter()
                    .map(|i| *i.downcast::<Factory<T>>().ok().unwrap());

                getter.factories.reserve_exact(len);
                getter.factories.extend(items_iter);
            },
            do_new_factory: |any_getter| {
                let getter: &mut AggregateGetter<T> = any_getter
                    .downcast_mut::<AggregateGetter<T>>().unwrap();

                box Factory::<Vec<T>>::new(getter.boxed_clone())
            }
        }
    }

    /// Return aggregated type.
    pub fn get_arg_type(&self) -> TypeDef {
        self.typedef.clone()
    }

    /// Push factory items into aggregate.
    ///
    /// Note that all items should already match contained aggregate type:
    /// if aggregate was created for `int`, all pushed factories should
    /// produce int. Otherwise this method will panic your app.
    pub fn push_items(&mut self, items: Vec<Box<Any>>) {
        (self.do_push_items)(&mut self.any_getter, items);
    }

    /// Produces factory usable as argument for other factories.
    ///
    /// If inner factories make `int` values, this method will make factory
    /// that makes `Vec<int>` values.
    pub fn new_factory(&mut self) -> Box<Any> {
        (self.do_new_factory)(&mut self.any_getter)
    }

    /// Returns type which is used as aggregate result.
    pub fn container_of<T: 'static>() -> TypeDef {
        TypeDef::of::<Vec<T>>()
    }
}

struct AggregateGetter<T: 'static> {
    factories: Vec<Factory<T>>,
}

impl<T> Clone for AggregateGetter<T> {
    fn clone(&self) -> AggregateGetter<T> {
        AggregateGetter::<T> {
            factories: self.factories.clone()
        }
    }
}

impl<T> AggregateGetter<T> {
    pub fn new() -> AggregateGetter<T> {
        AggregateGetter::<T> {
            factories: Vec::with_capacity(0)
        }
    }
}

impl<T> Getter<Vec<T>> for AggregateGetter<T> {
    fn take(&self) -> Vec<T> {

        // Reserve exact result size.
        let mut items = Vec::<T>::with_capacity(self.factories.len());

        // Construct results from factory results.
        items.extend(
            self.factories.iter()
                .map(|f| f.take())
        );

        items
    }

    fn boxed_clone(&self) -> Box<Getter<Vec<T>> + 'static> {
        box self.clone()
    }
}

#[cfg(test)]
mod test {
    use { argless_as_factory, metafactory, AsFactoryExt };
    use super::{ Aggregate };

    #[test]
    fn should_be_usable_as_vec_of_types() {
        let mut container = Aggregate::new::<int>();
        container.push_items(vec![
            argless_as_factory(5i),
            argless_as_factory(13i)
        ]);

        let parent_metafactory = metafactory(
            |items: Vec<int>|
                items.into_iter()
                    .map(|i| format!("{}", i))
                    .collect::<Vec<String>>()
                    .connect(", ")
        );

        let parent_getter = parent_metafactory
            .new(vec![
                container.new_factory()
            ]).ok().unwrap()
            .as_factory_of::<String>().unwrap()
        ;

        assert_eq!(parent_getter.take(), "5, 13");
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod get_repos_should {
    use super::*;

    #[test]
    fn return_repos_with_app_name_hello_world_library_and_language_rust_and_program_type_lib_and_user_intrepion(
    ) {
        let expected = vec![GitHubRepository {
            app_name: "hello-world-library".to_string(),
            client_type: None,
            language: ProgrammingLanguage::Rust,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_string(),
        }];

        let actual = get_repos(
            "intrepion",
            "hello-world",
            ProgrammingLanguage::Rust,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }
}

enum ProgrammingLanguage {
    Rust,
}

enum ProgramType {
    Library,
}

struct GitHubRepository {
    app_name: String,
    client_type: Option<String>,
    language: ProgrammingLanguage,
    program_type: ProgramType,
    server_type: Option<String>,
    user: String,
}

fn get_repos(user: &str, app_name: &str, language: ProgrammingLanguage, program_type: ProgramType) {
}
<commit_msg>green<commit_after>#[cfg(test)]
mod get_repos_should {
    use super::*;

    #[test]
    fn return_repos_with_app_name_hello_world_library_and_language_rust_and_program_type_lib_and_user_intrepion(
    ) {
        let expected = vec![GitHubRepository {
            app_name: "hello-world-library".to_string(),
            client_type: None,
            language: ProgrammingLanguage::Rust,
            program_type: ProgramType::Library,
            server_type: None,
            user: "intrepion".to_string(),
        }];

        let actual = get_repos(
            "intrepion",
            "hello-world",
            ProgrammingLanguage::Rust,
            ProgramType::Library,
        );

        assert_eq!(actual, expected);
    }
}

#[derive(Debug, PartialEq)]
enum ProgrammingLanguage {
    Rust,
}

#[derive(Debug, PartialEq)]
enum ProgramType {
    Library,
}

#[derive(Debug, PartialEq)]
struct GitHubRepository {
    app_name: String,
    client_type: Option<String>,
    language: ProgrammingLanguage,
    program_type: ProgramType,
    server_type: Option<String>,
    user: String,
}

fn get_repos(user: &str, app_name: &str, language: ProgrammingLanguage, program_type: ProgramType) -> Vec<GitHubRepository> {
    vec![GitHubRepository {
        app_name: "hello-world-library".to_string(),
        client_type: None,
        language: ProgrammingLanguage::Rust,
        program_type: ProgramType::Library,
        server_type: None,
        user: "intrepion".to_string()
    }]
}
<|endoftext|>


<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 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!()
    }
    fn unblock_account(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn mute_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        unimplemented!()
    }
    fn unmute_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        unimplemented!()
    }
    fn get_account_relationships(&self, query: RelationshipsQuery) -> Result<Vec<entities::Relationship>> {
        unimplemented!()
    }
    fn search_accounts(&self, query: SearchAccountsQuery) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
}
impl methods::Blocks for Client {
    fn fetch_blocks(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
}
impl methods::Favourites for Client {
    fn fetch_favourites(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
}
impl methods::FollowRequests for Client {
    fn fetch_follow_requests(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn authorize_follow_request(&self, form_data: String) -> Result<()> {
        unimplemented!()
    }
    fn reject_follow_request(&self, form_data: String) -> Result<()> {
        unimplemented!()
    }
}

impl methods::Follows for Client {
    fn follow_remote_user(&self, form_data: String) -> Result<entities::Account> {
        unimplemented!()
    }
}

impl methods::Instances for Client {
    fn get_instance(&self) -> Result<entities::Instance> {
        unimplemented!()
    }
}

impl methods::Media for Client {
    fn upload_media(&self, form_data: String) -> Result<entities::Attachment> {
        unimplemented!()
    }
}

impl methods::Mutes for Client {
    fn fetch_mutes(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
}

impl methods::Notifications for Client {
    fn fetch_notifications(&self, query: String) -> Result<Vec<entities::Notification>> {
        unimplemented!()
    }
    fn get_notification(&self, notification_id: NotificationID) -> Result<entities::Notification> {
        unimplemented!()
    }
    fn clear_notifications(&self) -> Result<()> {
        unimplemented!()
    }
}

impl methods::Reports for Client {
    fn fetching_reports(&self) -> Result<Vec<entities::Report>> {
        unimplemented!()
    }
    fn reporting_user(&self, form_data: String) -> Result<entities::Report> {
        unimplemented!()
    }
}

impl methods::Search for Client {
    fn search_content(&self, query: SearchContentsQuery) -> Result<entities::Results> {
        unimplemented!()
    }
}

impl methods::Statuses for Client {
    fn fetch_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn get_status_context(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn get_status_card(&self, status_id: StatusID) -> Result<entities::Card> {
        unimplemented!()
    }
    fn reblogged_by(&self, status_id: StatusID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn favourited_by(&self, status_id: StatusID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn post_status(&self, form_data: String) -> Result<entities::Status> {
        unimplemented!()
    }
    fn delete_status(&self, status_id: StatusID) -> Result<()> {
        unimplemented!()
    }
    fn reblog_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn unreblog_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn favourite_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn unfavourite_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
}

impl methods::Timelines for Client {
    fn home_timeline(&self, query: HomeTimelineQuery) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
    fn public_timeline(&self, query: TimelineQuery) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
    fn tag_timeline(&self, hashtag: TagID, query: TimelineQuery) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
}
<commit_msg>finish implementing skeleton code for Accounts API 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>> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::GetAccountStatuses(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 statuses.")
    }
    fn follow_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::FollowAccount(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error following account.")
    }
    fn unfollow_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::UnfollowAccount(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error unfollowing account.")
    }
    fn block_account(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::BlockAccount(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error blocking account.")
    }
    fn unblock_account(&self, account_id: AccountID) -> Result<Vec<entities::Account>> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::UnblockAccount(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error unblocking account.")
    }
    fn mute_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::MuteAccount(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error muting account.")
    }
    fn unmute_account(&self, account_id: AccountID) -> Result<entities::Relationship> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::UnmuteAccount(account_id);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error unmuting account.")
    }
    fn get_account_relationships(&self, query: RelationshipsQuery) -> Result<Vec<entities::Relationship>> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::GetAccountRelationships(query);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error getting account relationships.")
    }
    fn search_accounts(&self, query: SearchAccountsQuery) -> Result<Vec<entities::Account>> {
        let data = Arc::new(Mutex::new(Vec::new()));
        let endpoint = APIEndpoint::SearchAccounts(query);;
        self.send(endpoint, data.clone()).unwrap();
        let data = data.lock().unwrap();
        serde_json::from_slice(&data).chain_err(|| "Unexpected JSON error getting account search.")
    }
}
impl methods::Blocks for Client {
    fn fetch_blocks(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
}
impl methods::Favourites for Client {
    fn fetch_favourites(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
}
impl methods::FollowRequests for Client {
    fn fetch_follow_requests(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn authorize_follow_request(&self, form_data: String) -> Result<()> {
        unimplemented!()
    }
    fn reject_follow_request(&self, form_data: String) -> Result<()> {
        unimplemented!()
    }
}

impl methods::Follows for Client {
    fn follow_remote_user(&self, form_data: String) -> Result<entities::Account> {
        unimplemented!()
    }
}

impl methods::Instances for Client {
    fn get_instance(&self) -> Result<entities::Instance> {
        unimplemented!()
    }
}

impl methods::Media for Client {
    fn upload_media(&self, form_data: String) -> Result<entities::Attachment> {
        unimplemented!()
    }
}

impl methods::Mutes for Client {
    fn fetch_mutes(&self, query: String) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
}

impl methods::Notifications for Client {
    fn fetch_notifications(&self, query: String) -> Result<Vec<entities::Notification>> {
        unimplemented!()
    }
    fn get_notification(&self, notification_id: NotificationID) -> Result<entities::Notification> {
        unimplemented!()
    }
    fn clear_notifications(&self) -> Result<()> {
        unimplemented!()
    }
}

impl methods::Reports for Client {
    fn fetching_reports(&self) -> Result<Vec<entities::Report>> {
        unimplemented!()
    }
    fn reporting_user(&self, form_data: String) -> Result<entities::Report> {
        unimplemented!()
    }
}

impl methods::Search for Client {
    fn search_content(&self, query: SearchContentsQuery) -> Result<entities::Results> {
        unimplemented!()
    }
}

impl methods::Statuses for Client {
    fn fetch_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn get_status_context(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn get_status_card(&self, status_id: StatusID) -> Result<entities::Card> {
        unimplemented!()
    }
    fn reblogged_by(&self, status_id: StatusID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn favourited_by(&self, status_id: StatusID) -> Result<Vec<entities::Account>> {
        unimplemented!()
    }
    fn post_status(&self, form_data: String) -> Result<entities::Status> {
        unimplemented!()
    }
    fn delete_status(&self, status_id: StatusID) -> Result<()> {
        unimplemented!()
    }
    fn reblog_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn unreblog_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn favourite_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
    fn unfavourite_status(&self, status_id: StatusID) -> Result<entities::Status> {
        unimplemented!()
    }
}

impl methods::Timelines for Client {
    fn home_timeline(&self, query: HomeTimelineQuery) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
    fn public_timeline(&self, query: TimelineQuery) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
    fn tag_timeline(&self, hashtag: TagID, query: TimelineQuery) -> Result<Vec<entities::Status>> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use libc;
use std::{mem, ptr};
use std::ffi::CString;
use std::slice::from_raw_parts;

use events::Event;

use super::{events, ffi};
use super::XConnection;

#[derive(Debug)]
enum AxisType {
    HorizontalScroll,
    VerticalScroll
}

#[derive(Debug)]
struct Axis {
    id: i32,
    device_id: i32,
    axis_number: i32,
    axis_type: AxisType
}

#[derive(Debug)]
struct AxisValue {
    device_id: i32,
    axis_number: i32,
    value: f64
}

struct InputState {
    /// Last-seen cursor position within a window in (x, y)
    /// coordinates
    cursor_pos: (f64, f64),
    /// Last-seen positions of axes, used to report delta
    /// movements when a new absolute axis value is received
    axis_values: Vec<AxisValue>
}

pub struct XInputEventHandler {
    display: Arc<XConnection>,
    window: ffi::Window,
    ic: ffi::XIC,
    axis_list: Vec<Axis>,
    current_state: InputState
}

impl XInputEventHandler {
    pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC) -> XInputEventHandler {
        // query XInput support
        let mut opcode: libc::c_int = 0;
        let mut event: libc::c_int = 0;
        let mut error: libc::c_int = 0;
        let xinput_str = CString::new("XInputExtension").unwrap();

        unsafe {
            if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
                panic!("XInput not available")
            }
        }

        let mut xinput_major_ver = ffi::XI_2_Major;
        let mut xinput_minor_ver = ffi::XI_2_Minor;

        unsafe {
            if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
                panic!("Unable to determine XInput version");
            }
        }

        // specify the XInput events we want to receive.
        // Button clicks and mouse events are handled via XInput
        // events. Key presses are still handled via plain core
        // X11 events.
        let mut mask: [libc::c_uchar; 3] = [0; 3];
        let mut input_event_mask = ffi::XIEventMask {
            deviceid: ffi::XIAllMasterDevices,
            mask_len: mask.len() as i32,
            mask: mask.as_mut_ptr()
        };
        let events = &[
            ffi::XI_ButtonPress,
            ffi::XI_ButtonRelease,
            ffi::XI_Motion,
            ffi::XI_Enter,
            ffi::XI_Leave,
            ffi::XI_FocusIn,
            ffi::XI_FocusOut,
            ffi::XI_TouchBegin,
            ffi::XI_TouchUpdate,
            ffi::XI_TouchEnd,
        ];
        for event in events {
            ffi::XISetMask(&mut mask, *event);
        }

        unsafe {
            match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
                status if status as u8 == ffi::Success => (),
                err => panic!("Failed to select events {:?}", err)
            }
        }

        XInputEventHandler {
            display: display.clone(),
            window: window,
            ic: ic,
            axis_list: read_input_axis_info(display),
            current_state: InputState {
                cursor_pos: (0.0, 0.0),
                axis_values: Vec::new()
            }
        }
    }

    pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
        use events::Event::{KeyboardInput, ReceivedCharacter};
        use events::ElementState::{Pressed, Released};

        let mut translated_events = Vec::new();

        let state;
        if event.type_ == ffi::KeyPress {
            let raw_ev: *mut ffi::XKeyEvent = event;
            unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
            state = Pressed;
        } else {
            state = Released;
        }

        let mut kp_keysym = 0;

        let written = unsafe {
            use std::str;

            let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
            let raw_ev: *mut ffi::XKeyEvent = event;
            let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
            mem::transmute(buffer.as_mut_ptr()),
            buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());

            str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
        };

        for chr in written.chars() {
            translated_events.push(ReceivedCharacter(chr));
        }

        let mut keysym = unsafe {
            (self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
        };

        if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
            keysym = kp_keysym
        };

        let vkey = events::keycode_to_element(keysym as libc::c_uint);

        translated_events.push(KeyboardInput(state, event.keycode as u8, vkey));
        translated_events
    }

    pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
        use events::Event::{Focused, MouseInput, MouseMoved, MouseWheel};
        use events::ElementState::{Pressed, Released};
        use events::MouseButton::{Left, Right, Middle};
        use events::MouseScrollDelta::{PixelDelta, LineDelta};
        use events::{Touch, TouchPhase};

        match cookie.evtype {
            ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let state = if cookie.evtype == ffi::XI_ButtonPress {
                    Pressed
                } else {
                    Released
                };
                match event_data.detail as u32 {
                    ffi::Button1 => Some(MouseInput(state, Left)),
                    ffi::Button2 => Some(MouseInput(state, Middle)),
                    ffi::Button3 => Some(MouseInput(state, Right)),
                    ffi::Button4 | ffi::Button5 => {
                        if event_data.flags & ffi::XIPointerEmulated == 0 {
                            // scroll event from a traditional wheel with
                            // distinct 'clicks'
                            let delta = if event_data.detail as u32 == ffi::Button4 {
                                1.0
                            } else {
                                -1.0
                            };
                            Some(MouseWheel(LineDelta(0.0, delta)))
                        } else {
                            // emulated button event from a touch/smooth-scroll
                            // event. Ignore these events and handle scrolling
                            // via XI_Motion event handler instead
                            None
                        }
                    }
                    _ => None
                }
            },
            ffi::XI_Motion => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let axis_state = event_data.valuators;
                let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
                let mut axis_count = 0;

                let mut scroll_delta = (0.0, 0.0);
                for axis_id in 0..axis_state.mask_len {
                    if ffi::XIMaskIsSet(&mask, axis_id) {
                        let axis_value = unsafe{*axis_state.values.offset(axis_count)};
                        let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
                                                       &mut self.current_state.axis_values);
                        scroll_delta.0 += delta.0;
                        scroll_delta.1 += delta.1;
                        axis_count += 1;
                    }
                }

                if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
                    Some(MouseWheel(PixelDelta(scroll_delta.0 as f32, scroll_delta.1 as f32)))
                } else {
                    let new_cursor_pos = (event_data.event_x, event_data.event_y);
                    if new_cursor_pos != self.current_state.cursor_pos {
                        self.current_state.cursor_pos = new_cursor_pos;
                        Some(MouseMoved((new_cursor_pos.0 as i32, new_cursor_pos.1 as i32)))
                    } else {
                        None
                    }
                }
            },
            ffi::XI_Enter => {
                // axis movements whilst the cursor is outside the window
                // will alter the absolute value of the axes. We only want to
                // report changes in the axis value whilst the cursor is above
                // our window however, so clear the previous axis state whenever
                // the cursor re-enters the window
                self.current_state.axis_values.clear();
                None
            },
            ffi::XI_Leave => None,
            ffi::XI_FocusIn => Some(Focused(true)),
            ffi::XI_FocusOut => Some(Focused(false)),
            ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let phase = match cookie.evtype {
                    ffi::XI_TouchBegin => TouchPhase::Started,
                    ffi::XI_TouchUpdate => TouchPhase::Moved,
                    ffi::XI_TouchEnd => TouchPhase::Ended,
                    _ => unreachable!()
                };
                Some(Event::Touch(Touch {
                    phase: phase,
                    location: (event_data.event_x, event_data.event_y),
                    id: event_data.detail as u64,
                }))
            }
            _ => None
        }
    }
}

fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
    let mut axis_list = Vec::new();
    let mut device_count = 0;

    // only get events from the master devices which are 'attached'
    // to the keyboard or cursor
    let devices = unsafe{
        (display.xinput2.XIQueryDevice)(display.display, ffi::XIAllMasterDevices, &mut device_count)
    };
    for i in 0..device_count {
        let device = unsafe { *(devices.offset(i as isize)) };
        for k in 0..device.num_classes {
            let class = unsafe { *(device.classes.offset(k as isize)) };
            match unsafe { (*class)._type } {
                // Note that scroll axis
                // are reported both as 'XIScrollClass' and 'XIValuatorClass'
                // axes. For the moment we only care about scrolling axes.
                ffi::XIScrollClass => {
                    let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
                    axis_list.push(Axis{
                        id: scroll_class.sourceid,
                        device_id: device.deviceid,
                        axis_number: scroll_class.number,
                        axis_type: match scroll_class.scroll_type {
                            ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
                            ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
                            _ => { unreachable!() }
                        }
                    })
                },
                _ => {}
            }
        }
    }

    axis_list
}

/// Given an input motion event for an axis and the previous
/// state of the axes, return the horizontal/vertical
/// scroll deltas
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
                     axis_id: i32,
                     axis_value: f64,
                     axis_list: &[Axis],
                     prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
    let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
        prev_axis.device_id == event.sourceid &&
            prev_axis.axis_number == axis_id
    });
    let delta = match prev_value_pos {
        Some(idx) => axis_value - prev_axis_values[idx].value,
        None => 0.0
    };

    let new_axis_value = AxisValue{
        device_id: event.sourceid,
        axis_number: axis_id,
        value: axis_value
    };

    match prev_value_pos {
        Some(idx) => prev_axis_values[idx] = new_axis_value,
        None => prev_axis_values.push(new_axis_value)
    }

    let mut scroll_delta = (0.0, 0.0);

    for axis in axis_list.iter() {
        if axis.id == event.sourceid &&
            axis.axis_number == axis_id {
                match axis.axis_type {
                    AxisType::HorizontalScroll => scroll_delta.0 = delta,
                    AxisType::VerticalScroll => scroll_delta.1 = delta
                }
            }
    }

    scroll_delta
}

<commit_msg>Take XIScrollClassInfo::increment into account<commit_after>use std::sync::Arc;

use libc;
use std::{mem, ptr};
use std::ffi::CString;
use std::slice::from_raw_parts;

use events::Event;

use super::{events, ffi};
use super::XConnection;

#[derive(Debug)]
enum AxisType {
    HorizontalScroll,
    VerticalScroll
}

#[derive(Debug)]
struct Axis {
    id: i32,
    device_id: i32,
    axis_number: i32,
    axis_type: AxisType,
    scroll_increment: f64,
}

#[derive(Debug)]
struct AxisValue {
    device_id: i32,
    axis_number: i32,
    value: f64
}

struct InputState {
    /// Last-seen cursor position within a window in (x, y)
    /// coordinates
    cursor_pos: (f64, f64),
    /// Last-seen positions of axes, used to report delta
    /// movements when a new absolute axis value is received
    axis_values: Vec<AxisValue>
}

pub struct XInputEventHandler {
    display: Arc<XConnection>,
    window: ffi::Window,
    ic: ffi::XIC,
    axis_list: Vec<Axis>,
    current_state: InputState
}

impl XInputEventHandler {
    pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC) -> XInputEventHandler {
        // query XInput support
        let mut opcode: libc::c_int = 0;
        let mut event: libc::c_int = 0;
        let mut error: libc::c_int = 0;
        let xinput_str = CString::new("XInputExtension").unwrap();

        unsafe {
            if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
                panic!("XInput not available")
            }
        }

        let mut xinput_major_ver = ffi::XI_2_Major;
        let mut xinput_minor_ver = ffi::XI_2_Minor;

        unsafe {
            if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
                panic!("Unable to determine XInput version");
            }
        }

        // specify the XInput events we want to receive.
        // Button clicks and mouse events are handled via XInput
        // events. Key presses are still handled via plain core
        // X11 events.
        let mut mask: [libc::c_uchar; 3] = [0; 3];
        let mut input_event_mask = ffi::XIEventMask {
            deviceid: ffi::XIAllMasterDevices,
            mask_len: mask.len() as i32,
            mask: mask.as_mut_ptr()
        };
        let events = &[
            ffi::XI_ButtonPress,
            ffi::XI_ButtonRelease,
            ffi::XI_Motion,
            ffi::XI_Enter,
            ffi::XI_Leave,
            ffi::XI_FocusIn,
            ffi::XI_FocusOut,
            ffi::XI_TouchBegin,
            ffi::XI_TouchUpdate,
            ffi::XI_TouchEnd,
        ];
        for event in events {
            ffi::XISetMask(&mut mask, *event);
        }

        unsafe {
            match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
                status if status as u8 == ffi::Success => (),
                err => panic!("Failed to select events {:?}", err)
            }
        }

        XInputEventHandler {
            display: display.clone(),
            window: window,
            ic: ic,
            axis_list: read_input_axis_info(display),
            current_state: InputState {
                cursor_pos: (0.0, 0.0),
                axis_values: Vec::new()
            }
        }
    }

    pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
        use events::Event::{KeyboardInput, ReceivedCharacter};
        use events::ElementState::{Pressed, Released};

        let mut translated_events = Vec::new();

        let state;
        if event.type_ == ffi::KeyPress {
            let raw_ev: *mut ffi::XKeyEvent = event;
            unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
            state = Pressed;
        } else {
            state = Released;
        }

        let mut kp_keysym = 0;

        let written = unsafe {
            use std::str;

            let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
            let raw_ev: *mut ffi::XKeyEvent = event;
            let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
            mem::transmute(buffer.as_mut_ptr()),
            buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());

            str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
        };

        for chr in written.chars() {
            translated_events.push(ReceivedCharacter(chr));
        }

        let mut keysym = unsafe {
            (self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
        };

        if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
            keysym = kp_keysym
        };

        let vkey = events::keycode_to_element(keysym as libc::c_uint);

        translated_events.push(KeyboardInput(state, event.keycode as u8, vkey));
        translated_events
    }

    pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
        use events::Event::{Focused, MouseInput, MouseMoved, MouseWheel};
        use events::ElementState::{Pressed, Released};
        use events::MouseButton::{Left, Right, Middle};
        use events::MouseScrollDelta::LineDelta;
        use events::{Touch, TouchPhase};

        match cookie.evtype {
            ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let state = if cookie.evtype == ffi::XI_ButtonPress {
                    Pressed
                } else {
                    Released
                };
                match event_data.detail as u32 {
                    ffi::Button1 => Some(MouseInput(state, Left)),
                    ffi::Button2 => Some(MouseInput(state, Middle)),
                    ffi::Button3 => Some(MouseInput(state, Right)),
                    ffi::Button4 | ffi::Button5 => {
                        if event_data.flags & ffi::XIPointerEmulated == 0 {
                            // scroll event from a traditional wheel with
                            // distinct 'clicks'
                            let delta = if event_data.detail as u32 == ffi::Button4 {
                                1.0
                            } else {
                                -1.0
                            };
                            Some(MouseWheel(LineDelta(0.0, delta)))
                        } else {
                            // emulated button event from a touch/smooth-scroll
                            // event. Ignore these events and handle scrolling
                            // via XI_Motion event handler instead
                            None
                        }
                    }
                    _ => None
                }
            },
            ffi::XI_Motion => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let axis_state = event_data.valuators;
                let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
                let mut axis_count = 0;

                let mut scroll_delta = (0.0, 0.0);
                for axis_id in 0..axis_state.mask_len {
                    if ffi::XIMaskIsSet(&mask, axis_id) {
                        let axis_value = unsafe{*axis_state.values.offset(axis_count)};
                        let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
                                                       &mut self.current_state.axis_values);
                        scroll_delta.0 += delta.0;
                        scroll_delta.1 += delta.1;
                        axis_count += 1;
                    }
                }

                if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
                    Some(MouseWheel(LineDelta(scroll_delta.0 as f32, scroll_delta.1 as f32)))
                } else {
                    let new_cursor_pos = (event_data.event_x, event_data.event_y);
                    if new_cursor_pos != self.current_state.cursor_pos {
                        self.current_state.cursor_pos = new_cursor_pos;
                        Some(MouseMoved((new_cursor_pos.0 as i32, new_cursor_pos.1 as i32)))
                    } else {
                        None
                    }
                }
            },
            ffi::XI_Enter => {
                // axis movements whilst the cursor is outside the window
                // will alter the absolute value of the axes. We only want to
                // report changes in the axis value whilst the cursor is above
                // our window however, so clear the previous axis state whenever
                // the cursor re-enters the window
                self.current_state.axis_values.clear();
                None
            },
            ffi::XI_Leave => None,
            ffi::XI_FocusIn => Some(Focused(true)),
            ffi::XI_FocusOut => Some(Focused(false)),
            ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let phase = match cookie.evtype {
                    ffi::XI_TouchBegin => TouchPhase::Started,
                    ffi::XI_TouchUpdate => TouchPhase::Moved,
                    ffi::XI_TouchEnd => TouchPhase::Ended,
                    _ => unreachable!()
                };
                Some(Event::Touch(Touch {
                    phase: phase,
                    location: (event_data.event_x, event_data.event_y),
                    id: event_data.detail as u64,
                }))
            }
            _ => None
        }
    }
}

fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
    let mut axis_list = Vec::new();
    let mut device_count = 0;

    // only get events from the master devices which are 'attached'
    // to the keyboard or cursor
    let devices = unsafe{
        (display.xinput2.XIQueryDevice)(display.display, ffi::XIAllMasterDevices, &mut device_count)
    };
    for i in 0..device_count {
        let device = unsafe { *(devices.offset(i as isize)) };
        for k in 0..device.num_classes {
            let class = unsafe { *(device.classes.offset(k as isize)) };
            match unsafe { (*class)._type } {
                // Note that scroll axis
                // are reported both as 'XIScrollClass' and 'XIValuatorClass'
                // axes. For the moment we only care about scrolling axes.
                ffi::XIScrollClass => {
                    let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
                    axis_list.push(Axis{
                        id: scroll_class.sourceid,
                        device_id: device.deviceid,
                        axis_number: scroll_class.number,
                        axis_type: match scroll_class.scroll_type {
                            ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
                            ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
                            _ => { unreachable!() }
                        },
                        scroll_increment: scroll_class.increment,
                    })
                },
                _ => {}
            }
        }
    }

    axis_list
}

/// Given an input motion event for an axis and the previous
/// state of the axes, return the horizontal/vertical
/// scroll deltas
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
                     axis_id: i32,
                     axis_value: f64,
                     axis_list: &[Axis],
                     prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
    let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
        prev_axis.device_id == event.sourceid &&
            prev_axis.axis_number == axis_id
    });
    let delta = match prev_value_pos {
        Some(idx) => prev_axis_values[idx].value - axis_value,
        None => 0.0
    };

    let new_axis_value = AxisValue{
        device_id: event.sourceid,
        axis_number: axis_id,
        value: axis_value
    };

    match prev_value_pos {
        Some(idx) => prev_axis_values[idx] = new_axis_value,
        None => prev_axis_values.push(new_axis_value)
    }

    let mut scroll_delta = (0.0, 0.0);

    for axis in axis_list.iter() {
        if axis.id == event.sourceid &&
            axis.axis_number == axis_id {
                match axis.axis_type {
                    AxisType::HorizontalScroll => scroll_delta.0 = delta / axis.scroll_increment,
                    AxisType::VerticalScroll => scroll_delta.1 = delta / axis.scroll_increment
                }
            }
    }

    scroll_delta
}

<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! How to retreive data from an image within a shader.
//!
//! This module contains a struct named `Sampler` which describes how to get pixel data from
//! a texture.
//!
use std::mem;
use std::ptr;
use std::sync::Arc;

use device::Device;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Describes how to retreive data from an image within a shader.
pub struct Sampler {
    sampler: vk::Sampler,
    device: Arc<Device>,
}

// TODO: what's the story with VK_KHR_mirror_clamp_to_edge? Is it an extension or is it core?

impl Sampler {
    /// Creates a new `Sampler` with the given behavior.
    ///
    /// # Panic
    ///
    /// - Panicks if `max_anisotropy < 1.0`.
    /// - Panicks if `min_lod > max_lod`.
    ///
    // TODO: wrong error type returned
    pub fn new(device: &Arc<Device>, mag_filter: Filter, min_filter: Filter,
               mipmap_mode: MipmapMode, address_u: SamplerAddressMode,
               address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32,
               max_anisotropy: f32, min_lod: f32, max_lod: f32) -> Result<Arc<Sampler>, OomError>
    {
        assert!(max_anisotropy >= 1.0);
        // TODO: check limits
        assert!(min_lod <= max_lod);

        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: mag_filter as u32,
                minFilter: min_filter as u32,
                mipmapMode: mipmap_mode as u32,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: address_w as u32,
                mipLodBias: mip_lod_bias,
                anisotropyEnable: if max_anisotropy > 1.0 { vk::TRUE } else { vk::FALSE },
                maxAnisotropy: max_anisotropy,
                compareEnable: 0,       // FIXME:
                compareOp: 0,       // FIXME:
                minLod: min_lod,
                maxLod: max_lod,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::FALSE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }

    /// Creates a sampler with unnormalized coordinates. This means that texture coordinates won't
    /// range between `0.0` and `1.0` but use plain pixel offsets.
    ///
    /// Using an unnormalized sampler adds a few restrictions:
    ///
    /// - It can only be used with non-array 1D or 2D images.
    /// - It can only be used with images with a single mipmap.
    /// - Projection and offsets can't be used by shaders. Only the first mipmap can be accessed.
    ///
    // TODO: wrong error type returned
    pub fn unnormalized(device: &Arc<Device>, filter: Filter,
                        address_u: UnnormalizedSamplerAddressMode,
                        address_v: UnnormalizedSamplerAddressMode) -> Result<Arc<Sampler>, OomError>
    {
        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: filter as u32,
                minFilter: filter as u32,
                mipmapMode: vk::SAMPLER_MIPMAP_MODE_NEAREST,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,       // unused by the impl
                mipLodBias: 0.0,
                anisotropyEnable: vk::FALSE,
                maxAnisotropy: 0.0,
                compareEnable: vk::FALSE,
                compareOp: vk::COMPARE_OP_NEVER,
                minLod: 0.0,
                maxLod: 0.0,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::TRUE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }
}

unsafe impl VulkanObject for Sampler {
    type Object = vk::Sampler;

    #[inline]
    fn internal_object(&self) -> vk::Sampler {
        self.sampler
    }
}

impl Drop for Sampler {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySampler(self.device.internal_object(), self.sampler, ptr::null());
        }
    }
}

/// Describes how the color of each pixel should be determined.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum Filter {
    /// The four pixels whose center surround the requested coordinates are taken, then their
    /// values are interpolated.
    Linear = vk::FILTER_LINEAR,

    /// The pixel whose center is nearest to the requested coordinates is taken from the source
    /// and its value is returned as-is.
    Nearest = vk::FILTER_NEAREST,
}

/// Describes which mipmap from the source to use.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum MipmapMode {
    /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination.
    Nearest = vk::SAMPLER_MIPMAP_MODE_NEAREST,

    /// Take the two mipmaps whose dimensions are immediately inferior and superior to the
    /// dimensions of the destination, calculate the value for both, and interpolate them.
    Linear = vk::SAMPLER_MIPMAP_MODE_LINEAR,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum SamplerAddressMode {
    Repeat = vk::SAMPLER_ADDRESS_MODE_REPEAT,
    MirroredRepeat = vk::SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
    MirrorClampToEdge = vk::SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum UnnormalizedSamplerAddressMode {
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
}

#[cfg(test)]
mod tests {
    use sampler;

    #[test]
    fn create() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0,
                                      0.0, 2.0).unwrap();
    }

    #[test]
    #[should_panic]
    fn min_lod_inferior() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0, 5.0, 2.0);
    }

    #[test]
    #[should_panic]
    fn max_anisotropy() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 0.5, 0.0, 2.0);
    }
}
<commit_msg>Return a proper error when creating a sampler<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! How to retreive data from an image within a shader.
//!
//! This module contains a struct named `Sampler` which describes how to get pixel data from
//! a texture.
//!
use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;

use device::Device;
use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Describes how to retreive data from an image within a shader.
pub struct Sampler {
    sampler: vk::Sampler,
    device: Arc<Device>,
}

// TODO: what's the story with VK_KHR_mirror_clamp_to_edge? Is it an extension or is it core?

impl Sampler {
    /// Creates a new `Sampler` with the given behavior.
    ///
    /// # Panic
    ///
    /// - Panicks if `max_anisotropy < 1.0`.
    /// - Panicks if `min_lod > max_lod`.
    ///
    // TODO: wrong error type returned
    pub fn new(device: &Arc<Device>, mag_filter: Filter, min_filter: Filter,
               mipmap_mode: MipmapMode, address_u: SamplerAddressMode,
               address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32,
               max_anisotropy: f32, min_lod: f32, max_lod: f32)
               -> Result<Arc<Sampler>, SamplerCreationError>
    {
        assert!(max_anisotropy >= 1.0);
        // TODO: check limits
        assert!(min_lod <= max_lod);

        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: mag_filter as u32,
                minFilter: min_filter as u32,
                mipmapMode: mipmap_mode as u32,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: address_w as u32,
                mipLodBias: mip_lod_bias,
                anisotropyEnable: if max_anisotropy > 1.0 { vk::TRUE } else { vk::FALSE },
                maxAnisotropy: max_anisotropy,
                compareEnable: 0,       // FIXME:
                compareOp: 0,       // FIXME:
                minLod: min_lod,
                maxLod: max_lod,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::FALSE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }

    /// Creates a sampler with unnormalized coordinates. This means that texture coordinates won't
    /// range between `0.0` and `1.0` but use plain pixel offsets.
    ///
    /// Using an unnormalized sampler adds a few restrictions:
    ///
    /// - It can only be used with non-array 1D or 2D images.
    /// - It can only be used with images with a single mipmap.
    /// - Projection and offsets can't be used by shaders. Only the first mipmap can be accessed.
    ///
    // TODO: wrong error type returned
    pub fn unnormalized(device: &Arc<Device>, filter: Filter,
                        address_u: UnnormalizedSamplerAddressMode,
                        address_v: UnnormalizedSamplerAddressMode)
                        -> Result<Arc<Sampler>, SamplerCreationError>
    {
        let vk = device.pointers();

        let sampler = unsafe {
            let infos = vk::SamplerCreateInfo {
                sType: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                magFilter: filter as u32,
                minFilter: filter as u32,
                mipmapMode: vk::SAMPLER_MIPMAP_MODE_NEAREST,
                addressModeU: address_u as u32,
                addressModeV: address_v as u32,
                addressModeW: vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,       // unused by the impl
                mipLodBias: 0.0,
                anisotropyEnable: vk::FALSE,
                maxAnisotropy: 0.0,
                compareEnable: vk::FALSE,
                compareOp: vk::COMPARE_OP_NEVER,
                minLod: 0.0,
                maxLod: 0.0,
                borderColor: 0,     // FIXME:
                unnormalizedCoordinates: vk::TRUE,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSampler(device.internal_object(), &infos,
                                               ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Sampler {
            sampler: sampler,
            device: device.clone(),
        }))
    }
}

unsafe impl VulkanObject for Sampler {
    type Object = vk::Sampler;

    #[inline]
    fn internal_object(&self) -> vk::Sampler {
        self.sampler
    }
}

impl Drop for Sampler {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySampler(self.device.internal_object(), self.sampler, ptr::null());
        }
    }
}

/// Describes how the color of each pixel should be determined.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum Filter {
    /// The four pixels whose center surround the requested coordinates are taken, then their
    /// values are interpolated.
    Linear = vk::FILTER_LINEAR,

    /// The pixel whose center is nearest to the requested coordinates is taken from the source
    /// and its value is returned as-is.
    Nearest = vk::FILTER_NEAREST,
}

/// Describes which mipmap from the source to use.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum MipmapMode {
    /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination.
    Nearest = vk::SAMPLER_MIPMAP_MODE_NEAREST,

    /// Take the two mipmaps whose dimensions are immediately inferior and superior to the
    /// dimensions of the destination, calculate the value for both, and interpolate them.
    Linear = vk::SAMPLER_MIPMAP_MODE_LINEAR,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum SamplerAddressMode {
    Repeat = vk::SAMPLER_ADDRESS_MODE_REPEAT,
    MirroredRepeat = vk::SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
    MirrorClampToEdge = vk::SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum UnnormalizedSamplerAddressMode {
    ClampToEdge = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
    ClampToBorder = vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
}

/// Error that can happen when creating an instance.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SamplerCreationError {
    /// Not enough memory.
    OomError(OomError),
    /// Too many sampler objects have been created. You must destroy some before creating new ones.
    /// Note the specs guarantee that at least 4000 samplers can exist simultaneously.
    TooManyObjects,
}

impl error::Error for SamplerCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SamplerCreationError::OomError(_) => "not enough memory available",
            SamplerCreationError::TooManyObjects => "too many simultaneous sampler objects",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SamplerCreationError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for SamplerCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for SamplerCreationError {
    #[inline]
    fn from(err: OomError) -> SamplerCreationError {
        SamplerCreationError::OomError(err)
    }
}

impl From<Error> for SamplerCreationError {
    #[inline]
    fn from(err: Error) -> SamplerCreationError {
        match err {
            err @ Error::OutOfHostMemory => SamplerCreationError::OomError(OomError::from(err)),
            err @ Error::OutOfDeviceMemory => SamplerCreationError::OomError(OomError::from(err)),
            Error::TooManyObjects => SamplerCreationError::TooManyObjects,
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

#[cfg(test)]
mod tests {
    use sampler;

    #[test]
    fn create() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0,
                                      0.0, 2.0).unwrap();
    }

    #[test]
    #[should_panic]
    fn min_lod_inferior() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0, 5.0, 2.0);
    }

    #[test]
    #[should_panic]
    fn max_anisotropy() {
        let (device, queue) = gfx_dev_and_queue!();

        let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear,
                                      sampler::MipmapMode::Nearest,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat,
                                      sampler::SamplerAddressMode::Repeat, 1.0, 0.5, 0.0, 2.0);
    }
}
<|endoftext|>