<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Types and traits to navigate cursored collections.
//!
//! Much of this module can be considered an implementation detail; the main intended entry point
//! to this code is `CursorIter`, and that can just be used as an iterator to ignore the rest of
//! this module. The rest of it is available to make sure consumers of the API can understand
//! precisely what types come out of functions that return `CursorIter`.

use futures::{Future, Stream, Poll, Async};
use rustc_serialize::json;
use common::*;
use auth;
use error;
use error::Error::InvalidResponse;
use list;
use user;

///Trait to generalize over paginated views of API results.
///
///Types that implement Cursor are used as intermediate steps in [`CursorIter`][]'s Iterator
///implementation, to properly load the data from Twitter. Most of the time you don't need to deal
///with Cursor structs directly, but you can get them via `CursorIter`'s manual paging functionality.
///
///[`CursorIter`]: struct.CursorIter.html
pub trait Cursor {
    ///What type is being returned by the API call?
    type Item;

    ///Returns a numeric reference to the previous page of results.
    fn previous_cursor_id(&self) -> i64;
    ///Returns a numeric reference to the next page of results.
    fn next_cursor_id(&self) -> i64;
    ///Consumes the cursor and returns the collection of results from inside.
    fn into_inner(self) -> Vec<Self::Item>;
}

///Represents a single-page view into a list of users.
///
///This type is intended to be used in the background by [`CursorIter`][] to hold an intermediate
///list of users to iterate over. See that struct's documentation for details.
///
///[`CursorIter`]: struct.CursorIter.html
pub struct UserCursor {
    ///Numeric reference to the previous page of results.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results.
    pub next_cursor: i64,
    ///The list of users in this page of results.
    pub users: Vec<user::TwitterUser>,
}

impl FromJson for UserCursor {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("UserCursor received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, previous_cursor);
        field_present!(input, next_cursor);
        field_present!(input, users);

        Ok(UserCursor {
            previous_cursor: try!(field(input, "previous_cursor")),
            next_cursor: try!(field(input, "next_cursor")),
            users: try!(field(input, "users")),
        })
    }
}

impl Cursor for UserCursor {
    type Item = user::TwitterUser;

    fn previous_cursor_id(&self) -> i64 {
        self.previous_cursor
    }

    fn next_cursor_id(&self) -> i64 {
        self.next_cursor
    }

    fn into_inner(self) -> Vec<Self::Item> {
        self.users
    }
}

///Represents a single-page view into a list of user IDs.
///
///This type is intended to be used in the background by [`CursorIter`][] to hold an intermediate
///list of users to iterate over. See that struct's documentation for details.
///
///[`CursorIter`]: struct.CursorIter.html
pub struct IDCursor {
    ///Numeric reference to the previous page of results.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results.
    pub next_cursor: i64,
    ///The list of user IDs in this page of results.
    pub ids: Vec<u64>,
}

impl FromJson for IDCursor {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("IDCursor received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, previous_cursor);
        field_present!(input, next_cursor);
        field_present!(input, ids);

        Ok(IDCursor {
            previous_cursor: try!(field(input, "previous_cursor")),
            next_cursor: try!(field(input, "next_cursor")),
            ids: try!(field(input, "ids")),
        })
    }
}

impl Cursor for IDCursor {
    type Item = u64;

    fn previous_cursor_id(&self) -> i64 {
        self.previous_cursor
    }

    fn next_cursor_id(&self) -> i64 {
        self.next_cursor
    }

    fn into_inner(self) -> Vec<Self::Item> {
        self.ids
    }
}

///Represents a single-page view into a list of lists.
pub struct ListCursor {
    ///Numeric reference to the previous page of results.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results.
    pub next_cursor: i64,
    ///The list of lists in this page of results.
    pub lists: Vec<list::List>,
}

impl FromJson for ListCursor {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("ListCursor received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, previous_cursor);
        field_present!(input, next_cursor);
        field_present!(input, lists);

        Ok(ListCursor {
            previous_cursor: try!(field(input, "previous_cursor")),
            next_cursor: try!(field(input, "next_cursor")),
            lists: try!(field(input, "lists")),
        })
    }
}

impl Cursor for ListCursor {
    type Item = list::List;

    fn previous_cursor_id(&self) -> i64 {
        self.previous_cursor
    }

    fn next_cursor_id(&self) -> i64 {
        self.next_cursor
    }

    fn into_inner(self) -> Vec<Self::Item> {
        self.lists
    }
}

///Represents a paginated list of results, such as the users who follow a specific user or the
///lists owned by that user.
///
///This struct is returned by various functions and is meant to be used as an iterator. This means
///that all the standard iterator adaptors can be used to work with the results:
///
///```rust,no_run
///# let token = egg_mode::Token::Access {
///#     consumer: egg_mode::KeyPair::new("", ""),
///#     access: egg_mode::KeyPair::new("", ""),
///# };
///for name in egg_mode::user::followers_of("rustlang", &token)
///                                        .map(|u| u.unwrap().response.screen_name).take(10) {
///    println!("{}", name);
///}
///```
///
///You can even collect the results, letting you get one set of rate-limit information for the
///entire search setup:
///
///```rust,no_run
///# let token = egg_mode::Token::Access {
///#     consumer: egg_mode::KeyPair::new("", ""),
///#     access: egg_mode::KeyPair::new("", ""),
///# };
///use egg_mode::Response;
///use egg_mode::user::TwitterUser;
///use egg_mode::error::Error;
///
///let names: Result<Response<Vec<TwitterUser>>, Error> =
///    egg_mode::user::followers_of("rustlang", &token).take(10).collect();
///```
///
///`CursorIter` has a couple adaptors of its own that you can use before consuming it.
///`with_page_size` will let you set how many users are pulled in with a single network call, and
///`start_at_page` lets you start your search at a specific page. Calling either of these after
///starting iteration will clear any current results.
///
///(A note about `with_page_size`/`page_size`: While the `CursorIter` struct always has this method
///and field available, not every cursored call supports changing page size. Check the individual
///method documentation for notes on what page sizes are allowed.)
///
///The type returned by the iterator is `Result<Response<T::Item>, Error>`, so network errors,
///rate-limit errors and other issues are passed directly through to `next()`. This also means that
///getting an error while iterating doesn't mean you're at the end of the list; you can wait for
///the network connection to return or for the rate limit to refresh before trying again.
///
///## Manual paging
///
///The iterator works by lazily loading a page of results at a time (with size set by
///`with_page_size` or by directly assigning `page_size` for applicable calls) in the background
///whenever you ask for the next result. This can be nice, but it also means that you can lose
///track of when your loop will block for the next page of results. This is where the extra fields
///and methods on `UserSearch` come in. By using `call()`, you can get the cursor struct directly
///from Twitter.  With that you can iterate over the results and page forward and backward as
///needed:
///
///```rust,no_run
///# let token = egg_mode::Token::Access {
///#     consumer: egg_mode::KeyPair::new("", ""),
///#     access: egg_mode::KeyPair::new("", ""),
///# };
///let mut list = egg_mode::user::followers_of("rustlang", &token).with_page_size(20);
///let resp = list.call().unwrap();
///
///for user in resp.response.users {
///    println!("{} (@{})", user.name, user.screen_name);
///}
///
///list.next_cursor = resp.response.next_cursor;
///let resp = list.call().unwrap();
///
///for user in resp.response.users {
///    println!("{} (@{})", user.name, user.screen_name);
///}
///```
#[must_use = "cursor iterators are lazy and do nothing unless consumed"]
pub struct CursorIter<'a, T>
    where T: Cursor + FromJson + 'a
{
    link: &'static str,
    token: &'a auth::Token,
    handle: &'a Handle,
    params_base: Option<ParamList<'a>>,
    ///The number of results returned in one network call.
    ///
    ///Certain calls set their own minimums and maximums for what this value can be. Furthermore,
    ///some calls don't allow you to set the size of the pages at all. Refer to the individual
    ///methods' documentation for specifics.
    pub page_size: Option<i32>,
    ///Numeric reference to the previous page of results.
    ///
    ///This value is intended to be automatically set and used as part of this struct's Iterator
    ///implementation. It is made available for those who wish to manually manage network calls and
    ///pagination.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results. A value of zero indicates that the current
    ///page of results is the last page of the cursor.
    ///
    ///This value is intended to be automatically set and used as part of this struct's Iterator
    ///implementation. It is made available for those who wish to manually manage network calls and
    ///pagination.
    pub next_cursor: i64,
    loader: Option<FutureResponse<'a, T>>,
    iter: Option<ResponseIter<T::Item>>,
}

impl<'a, T> CursorIter<'a, T>
    where T: Cursor + FromJson + 'a
{
    ///Sets the number of results returned in a single network call.
    ///
    ///Certain calls set their own minimums and maximums for what this value can be. Furthermore,
    ///some calls don't allow you to set the size of the pages at all. Refer to the individual
    ///methods' documentation for specifics. If this method is called for a response that does not
    ///accept changing the page size, no change to the underlying struct will occur.
    ///
    ///Calling this function will invalidate any current results, if any were previously loaded.
    pub fn with_page_size(self, page_size: i32) -> CursorIter<'a, T> {
        if self.page_size.is_some() {
            CursorIter {
                page_size: Some(page_size),
                previous_cursor: -1,
                next_cursor: -1,
                loader: None,
                iter: None,
                ..self
            }
        }
        else {
            self
        }
    }

    ///Loads the next page of results.
    ///
    ///This is intended to be used as part of this struct's Iterator implementation. It is provided
    ///as a convenience for those who wish to manage network calls and pagination manually.
    pub fn call(&self) -> FutureResponse<'a, T> {
        let mut params = self.params_base.as_ref().cloned().unwrap_or_default();

        add_param(&mut params, "cursor", self.next_cursor.to_string());
        if let Some(count) = self.page_size {
            add_param(&mut params, "count", count.to_string());
        }

        let req = auth::get(self.link, self.token, Some(&params));

        make_parsed_future(self.handle, req)
    }

    ///Creates a new instance of CursorIter, with the given parameters and empty initial results.
    ///
    ///This is essentially an internal infrastructure function, not meant to be used from consumer
    ///code.
    #[doc(hidden)]
    pub fn new(link: &'static str, token: &'a auth::Token, handle: &'a Handle,
               params_base: Option<ParamList<'a>>, page_size: Option<i32>) -> CursorIter<'a, T> {
        CursorIter {
            link: link,
            token: token,
            handle: handle,
            params_base: params_base,
            page_size: page_size,
            previous_cursor: -1,
            next_cursor: -1,
            loader: None,
            iter: None,
        }
    }
}

impl<'a, T> Stream for CursorIter<'a, T>
    where T: Cursor + FromJson + 'a
{
    type Item = Response<T::Item>;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(mut fut) = self.loader.take() {
            match fut.poll() {
                Ok(Async::NotReady) => {
                    self.loader = Some(fut);
                    return Ok(Async::NotReady);
                }
                Ok(Async::Ready(resp)) => {
                    self.previous_cursor = resp.previous_cursor_id();
                    self.next_cursor = resp.next_cursor_id();

                    let resp = Response::map(resp, |r| r.into_inner());

                    let mut iter = resp.into_iter();
                    let first = iter.next();
                    self.iter = Some(iter);

                    match first {
                        Some(item) => return Ok(Async::Ready(Some(item))),
                        None => return Ok(Async::Ready(None)),
                    }
                }
                Err(e) => return Err(e),
            }
        }

        if let Some(ref mut results) = self.iter {
            if let Some(item) = results.next() {
                return Ok(Async::Ready(Some(item)));
            }
            else if self.next_cursor == 0 {
                return Ok(Async::Ready(None));
            }
        }

        self.loader = Some(self.call());
        self.poll()
    }
}
<commit_msg>update cursor docs<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Types and traits to navigate cursored collections.
//!
//! Much of this module can be considered an implementation detail; the main intended entry point
//! to this code is `CursorIter`, and that can just be used as a stream to ignore the rest of this
//! module. The rest of it is available to make sure consumers of the API can understand precisely
//! what types come out of functions that return `CursorIter`.

use futures::{Future, Stream, Poll, Async};
use rustc_serialize::json;
use common::*;
use auth;
use error;
use error::Error::InvalidResponse;
use list;
use user;

///Trait to generalize over paginated views of API results.
///
///Types that implement Cursor are used as intermediate steps in [`CursorIter`][]'s Stream
///implementation, to properly load the data from Twitter. Most of the time you don't need to deal
///with Cursor structs directly, but you can get them via `CursorIter`'s manual paging
///functionality.
///
///[`CursorIter`]: struct.CursorIter.html
pub trait Cursor {
    ///What type is being returned by the API call?
    type Item;

    ///Returns a numeric reference to the previous page of results.
    fn previous_cursor_id(&self) -> i64;
    ///Returns a numeric reference to the next page of results.
    fn next_cursor_id(&self) -> i64;
    ///Consumes the cursor and returns the collection of results from inside.
    fn into_inner(self) -> Vec<Self::Item>;
}

///Represents a single-page view into a list of users.
///
///This type is intended to be used in the background by [`CursorIter`][] to hold an intermediate
///list of users to iterate over. See that struct's documentation for details.
///
///[`CursorIter`]: struct.CursorIter.html
pub struct UserCursor {
    ///Numeric reference to the previous page of results.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results.
    pub next_cursor: i64,
    ///The list of users in this page of results.
    pub users: Vec<user::TwitterUser>,
}

impl FromJson for UserCursor {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("UserCursor received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, previous_cursor);
        field_present!(input, next_cursor);
        field_present!(input, users);

        Ok(UserCursor {
            previous_cursor: try!(field(input, "previous_cursor")),
            next_cursor: try!(field(input, "next_cursor")),
            users: try!(field(input, "users")),
        })
    }
}

impl Cursor for UserCursor {
    type Item = user::TwitterUser;

    fn previous_cursor_id(&self) -> i64 {
        self.previous_cursor
    }

    fn next_cursor_id(&self) -> i64 {
        self.next_cursor
    }

    fn into_inner(self) -> Vec<Self::Item> {
        self.users
    }
}

///Represents a single-page view into a list of IDs.
///
///This type is intended to be used in the background by [`CursorIter`][] to hold an intermediate
///list of IDs to iterate over. See that struct's documentation for details.
///
///[`CursorIter`]: struct.CursorIter.html
pub struct IDCursor {
    ///Numeric reference to the previous page of results.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results.
    pub next_cursor: i64,
    ///The list of user IDs in this page of results.
    pub ids: Vec<u64>,
}

impl FromJson for IDCursor {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("IDCursor received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, previous_cursor);
        field_present!(input, next_cursor);
        field_present!(input, ids);

        Ok(IDCursor {
            previous_cursor: try!(field(input, "previous_cursor")),
            next_cursor: try!(field(input, "next_cursor")),
            ids: try!(field(input, "ids")),
        })
    }
}

impl Cursor for IDCursor {
    type Item = u64;

    fn previous_cursor_id(&self) -> i64 {
        self.previous_cursor
    }

    fn next_cursor_id(&self) -> i64 {
        self.next_cursor
    }

    fn into_inner(self) -> Vec<Self::Item> {
        self.ids
    }
}

///Represents a single-page view into a list of lists.
///
///This type is intended to be used in the background by [`CursorIter`][] to hold an intermediate
///list of lists to iterate over. See that struct's documentation for details.
///
///[`CursorIter`]: struct.CursorIter.html
pub struct ListCursor {
    ///Numeric reference to the previous page of results.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results.
    pub next_cursor: i64,
    ///The list of lists in this page of results.
    pub lists: Vec<list::List>,
}

impl FromJson for ListCursor {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("ListCursor received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, previous_cursor);
        field_present!(input, next_cursor);
        field_present!(input, lists);

        Ok(ListCursor {
            previous_cursor: try!(field(input, "previous_cursor")),
            next_cursor: try!(field(input, "next_cursor")),
            lists: try!(field(input, "lists")),
        })
    }
}

impl Cursor for ListCursor {
    type Item = list::List;

    fn previous_cursor_id(&self) -> i64 {
        self.previous_cursor
    }

    fn next_cursor_id(&self) -> i64 {
        self.next_cursor
    }

    fn into_inner(self) -> Vec<Self::Item> {
        self.lists
    }
}

/// Represents a paginated list of results, such as the users who follow a specific user or the
/// lists owned by that user.
///
/// This struct is given by several methods in this library, whenever Twitter would return a
/// cursored list of items. It implements the `Stream` trait, loading items in batches so that
/// several can be immedately returned whenever a single network call completes.
///
/// ```rust,no_run
/// # extern crate egg_mode; extern crate tokio_core; extern crate futures;
/// # use egg_mode::Token; use tokio_core::reactor::{Core, Handle};
/// use futures::Stream;
///
/// # fn main() {
/// # let (token, mut core, handle): (Token, Core, Handle) = unimplemented!();
/// core.run(egg_mode::user::followers_of("rustlang", &token, &handle).take(10).for_each(|resp| {
///     println!("{}", resp.screen_name);
///     Ok(())
/// })).unwrap();
/// # }
/// ```
///
/// You can even collect the results, letting you get one set of rate-limit information for the
/// entire search setup:
///
/// ```rust,no_run
/// # extern crate egg_mode; extern crate tokio_core; extern crate futures;
/// # use egg_mode::Token; use tokio_core::reactor::{Core, Handle};
/// # fn main() {
/// # let (token, mut core, handle): (Token, Core, Handle) = unimplemented!();
/// use futures::Stream;
/// use egg_mode::Response;
/// use egg_mode::user::TwitterUser;
/// use egg_mode::error::Error;
///
/// // Because Streams don't have a FromIterator adaptor, we load all the responses first, then
/// // collect them into the final Vec
/// let names: Result<Response<Vec<TwitterUser>>, Error> =
///     core.run(egg_mode::user::followers_of("rustlang", &token, &handle).take(10).collect())
///         .map(|resp| resp.into_iter().collect());
/// # }
/// ```
///
/// `CursorIter` has an adaptor of its own, `with_page_size`, that you can use before consuming it.
/// `with_page_size` will let you set how many users are pulled in with a single network call.
/// Calling it after starting iteration will clear any current results.
///
/// (A note about `with_page_size`/`page_size`: While the `CursorIter` struct always has this method
/// and field available, not every cursored call supports changing page size. Check the individual
/// method documentation for notes on what page sizes are allowed.)
///
/// The `Stream` implementation yields `Response<T::Item>` on a successful iteration, and `Error`
/// for errors, so network errors, rate-limit errors and other issues are passed directly through
/// in `poll()`. The `Stream` implementation will allow you to poll again after an error to
/// re-initiate the late network call; this way, you can wait for your network connection to return
/// or for your rate limit to refresh and try again with the same state.
///
/// ## Manual paging
///
/// The `Stream` implementation works by loading in a page of results (with size set by the
/// method's default or by `with_page_size`/the `page_size` field) when it's polled, and serving
/// the individual elements from that locally-cached page until it runs out. This can be nice, but
/// it also means that your only warning that something involves a network call is that the stream
/// returns `Ok(Async::NotReady)`. If you want to know that ahead of time, that's where the
/// `call()` method comes in. By using `call()`, you can get the cursor struct directly from
/// Twitter. With that you can iterate over the results and page forward and backward as needed:
///
/// ```rust,no_run
/// # extern crate egg_mode; extern crate tokio_core;
/// # use egg_mode::Token; use tokio_core::reactor::{Core, Handle};
/// # fn main() {
/// # let (token, mut core, handle): (Token, Core, Handle) = unimplemented!();
/// let mut list = egg_mode::user::followers_of("rustlang", &token, &handle).with_page_size(20);
/// let resp = core.run(list.call()).unwrap();
///
/// for user in resp.response.users {
///     println!("{} (@{})", user.name, user.screen_name);
/// }
///
/// list.next_cursor = resp.response.next_cursor;
/// let resp = core.run(list.call()).unwrap();
///
/// for user in resp.response.users {
///     println!("{} (@{})", user.name, user.screen_name);
/// }
/// # }
/// ```
#[must_use = "cursor iterators are lazy and do nothing unless consumed"]
pub struct CursorIter<'a, T>
    where T: Cursor + FromJson + 'a
{
    link: &'static str,
    token: &'a auth::Token,
    handle: &'a Handle,
    params_base: Option<ParamList<'a>>,
    ///The number of results returned in one network call.
    ///
    ///Certain calls set their own minimums and maximums for what this value can be. Furthermore,
    ///some calls don't allow you to set the size of the pages at all. Refer to the individual
    ///methods' documentation for specifics.
    pub page_size: Option<i32>,
    ///Numeric reference to the previous page of results. A value of zero indicates that the
    ///current page of results is the first page of the cursor.
    ///
    ///This value is intended to be automatically set and used as part of this struct's Iterator
    ///implementation. It is made available for those who wish to manually manage network calls and
    ///pagination.
    pub previous_cursor: i64,
    ///Numeric reference to the next page of results. A value of zero indicates that the current
    ///page of results is the last page of the cursor.
    ///
    ///This value is intended to be automatically set and used as part of this struct's Iterator
    ///implementation. It is made available for those who wish to manually manage network calls and
    ///pagination.
    pub next_cursor: i64,
    loader: Option<FutureResponse<'a, T>>,
    iter: Option<ResponseIter<T::Item>>,
}

impl<'a, T> CursorIter<'a, T>
    where T: Cursor + FromJson + 'a
{
    ///Sets the number of results returned in a single network call.
    ///
    ///Certain calls set their own minimums and maximums for what this value can be. Furthermore,
    ///some calls don't allow you to set the size of the pages at all. Refer to the individual
    ///methods' documentation for specifics. If this method is called for a response that does not
    ///accept changing the page size, no change to the underlying struct will occur.
    ///
    ///Calling this function will invalidate any current results, if any were previously loaded.
    pub fn with_page_size(self, page_size: i32) -> CursorIter<'a, T> {
        if self.page_size.is_some() {
            CursorIter {
                page_size: Some(page_size),
                previous_cursor: -1,
                next_cursor: -1,
                loader: None,
                iter: None,
                ..self
            }
        }
        else {
            self
        }
    }

    ///Loads the next page of results.
    ///
    ///This is intended to be used as part of this struct's Iterator implementation. It is provided
    ///as a convenience for those who wish to manage network calls and pagination manually.
    pub fn call(&self) -> FutureResponse<'a, T> {
        let mut params = self.params_base.as_ref().cloned().unwrap_or_default();

        add_param(&mut params, "cursor", self.next_cursor.to_string());
        if let Some(count) = self.page_size {
            add_param(&mut params, "count", count.to_string());
        }

        let req = auth::get(self.link, self.token, Some(&params));

        make_parsed_future(self.handle, req)
    }

    ///Creates a new instance of CursorIter, with the given parameters and empty initial results.
    ///
    ///This is essentially an internal infrastructure function, not meant to be used from consumer
    ///code.
    #[doc(hidden)]
    pub fn new(link: &'static str, token: &'a auth::Token, handle: &'a Handle,
               params_base: Option<ParamList<'a>>, page_size: Option<i32>) -> CursorIter<'a, T> {
        CursorIter {
            link: link,
            token: token,
            handle: handle,
            params_base: params_base,
            page_size: page_size,
            previous_cursor: -1,
            next_cursor: -1,
            loader: None,
            iter: None,
        }
    }
}

impl<'a, T> Stream for CursorIter<'a, T>
    where T: Cursor + FromJson + 'a
{
    type Item = Response<T::Item>;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(mut fut) = self.loader.take() {
            match fut.poll() {
                Ok(Async::NotReady) => {
                    self.loader = Some(fut);
                    return Ok(Async::NotReady);
                }
                Ok(Async::Ready(resp)) => {
                    self.previous_cursor = resp.previous_cursor_id();
                    self.next_cursor = resp.next_cursor_id();

                    let resp = Response::map(resp, |r| r.into_inner());

                    let mut iter = resp.into_iter();
                    let first = iter.next();
                    self.iter = Some(iter);

                    match first {
                        Some(item) => return Ok(Async::Ready(Some(item))),
                        None => return Ok(Async::Ready(None)),
                    }
                }
                Err(e) => return Err(e),
            }
        }

        if let Some(ref mut results) = self.iter {
            if let Some(item) = results.next() {
                return Ok(Async::Ready(Some(item)));
            }
            else if self.next_cursor == 0 {
                return Ok(Async::Ready(None));
            }
        }

        self.loader = Some(self.call());
        self.poll()
    }
}
<|endoftext|>


<commit_before>/// Rust TNetStrings serialization library.

export tnetstring;
export str;
export int;
export float;
export bool;
export null;
export map;
export vec;
export to_writer;
export to_bytes;
export to_str;
export from_reader;
export from_bytes;
export from_str;

/// Represents a tnetstring value.
enum tnetstring {
    str(@~[u8]),
    int(int),
    float(float),
    bool(bool),
    null,
    map(map::hashmap<~[u8], tnetstring>),
    vec(@~[tnetstring]),
}

/// Serializes a tnetstring value into a io::Writer.
fn to_writer(writer: io::Writer, tnetstring: tnetstring) {
    fn write_str(wr: io::Writer, s: &[u8]) {
        wr.write_str(#fmt("%u:", s.len()));
        wr.write(s);
        wr.write_char(',');
    }

    match tnetstring {
        str(s) => write_str(writer, *s),
        int(i) => {
            let s = int::str(i);
            writer.write_str(#fmt("%u:%s#", s.len(), s));
        }
        float(f) => {
            let s = float::to_str(f, 6u);
            writer.write_str(#fmt("%u:%s^", s.len(), s));
        }
        bool(b) => {
            let s = bool::to_str(b);
            writer.write_str(#fmt("%u:%s!", s.len(), s));
        }
        map(m) => {
            let buf = io::mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            for m.each |key, value| {
                write_str(wr, key);
                to_writer(wr, value);
            }
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", payload.len()));
            writer.write(payload);
            writer.write_char('}');
        }
        vec(v) => {
            let buf = io::mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            for v.each |e| { to_writer(wr, e) }
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", payload.len()));
            writer.write(payload);
            writer.write_char(']');
        }
        null => writer.write_str("0:~"),
    }
}

/// Serializes a tnetstring value into a byte string.
fn to_bytes(tnetstring: tnetstring) -> ~[u8] {
    let buf = io::mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, tnetstring);
    io::mem_buffer_buf(buf)
}

/// Serializes a tnetstring value into a string.
fn to_str(tnetstring: tnetstring) -> ~str {
    let buf = io::mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, tnetstring);
    io::mem_buffer_str(buf)
}

/// Deserializes a tnetstring value from an io::Reader.
fn from_reader(reader: io::Reader) -> Option<tnetstring> {
    assert !reader.eof();

    let mut c = reader.read_byte();
    let mut len = 0u;

    // Note that netstring spec explicitly forbids padding zeros.
    // If the first char is zero, it must be the only char.
    if c < '0' as int || c > '9' as int {
        fail ~"Not a tnetstring: invalid or missing length prefix";
    } else if c == '0' as int {
        c = reader.read_byte();
    } else {
        loop {
            len = (10u * len) + ((c as uint) - ('0' as uint));

            if reader.eof() {
                fail ~"Not a tnetstring: invalid or missing length prefix";
            }
            c = reader.read_byte();

            if c < '0' as int || c > '9' as int {
                break;
            }
        }
    }

    // Validate end-of-length-prefix marker.
    if c != ':' as int {
        fail ~"Not a tnetstring: missing length prefix";
    }

    // Read the data plus terminating type tag.
    let payload = reader.read_bytes(len);

    if payload.len() != len {
        fail ~"Not a tnetstring: invalid length prefix";
    }

    if reader.eof() {
        fail ~"Not a tnetstring: missing type tag";
    }

    match reader.read_byte() as char {
      '#' => {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        int::from_str(s).map(|v| int(v))
      }
      '}' => Some(map(parse_map(payload))),
      ']' => Some(vec(parse_vec(payload))),
      '!' => {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        bool::from_str(s).map(|v| bool(v))
      }
      '^' => {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        float::from_str(s).map(|v| float(v))
      }
      '~' => {
        assert payload.len() == 0u;
        Some(null)
      }
      ',' => Some(str(@payload)),
      c => {
        let s = str::from_char(c);
        fail #fmt("Invalid payload type: %?", s)
      }
    }
}

fn parse_vec(data: ~[u8]) -> @~[tnetstring] {
    if data.len() == 0u { return @~[]; }

    do io::with_bytes_reader(data) |reader| {
        let result = DVec();

        let value = from_reader(reader);
        assert value.is_some();

        result.push(value.get());

        while !reader.eof() {
            let value = from_reader(reader);
            assert value.is_some();
            result.push(value.get());
        }

        @vec::from_mut(dvec::unwrap(result))
    }
}

fn parse_pair(reader: io::Reader) -> (@~[u8], tnetstring) {
    let key = from_reader(reader);
    assert key.is_some();
    assert !reader.eof();

    match key {
      Some(str(key)) => {
        let value = from_reader(reader);
        assert value.is_some();

        (key, value.get())
      }
      _ => fail ~"Keys can only be strings.",
    }
}

fn parse_map(data: ~[u8]) -> map::hashmap<~[u8], tnetstring> {
    if data.len() == 0u { return map::bytes_hash(); }

    do io::with_bytes_reader(data) |reader| {
        let result = map::bytes_hash();

        let (key, value) = parse_pair(reader);
        result.insert(copy *key, value);

        while !reader.eof() {
            let (key, value) = parse_pair(reader);
            result.insert(copy *key, value);
        }

        result
    }
}

/// Deserializes a tnetstring value from a byte string.
fn from_bytes(data: ~[u8]) -> (Option<tnetstring>, ~[u8]) {
    do io::with_bytes_reader(data) |reader| {
        let tnetstring = from_reader(reader);
        (tnetstring, reader.read_whole_stream())
    }
}

/// Deserializes a tnetstring value from a string.
fn from_str(data: &str) -> (Option<tnetstring>, ~str) {
    do io::with_str_reader(data) |rdr| {
        let tnetstring = from_reader(rdr);
        let bytes = rdr.read_whole_stream();
        (tnetstring, str::from_bytes(bytes))
    }

}

/// Test the equality between two tnetstring values
fn eq(t0: tnetstring, t1: tnetstring) -> bool {
    match (t0, t1) {
        (str(s0), str(s1)) => s0 == s1,
        (int(i0), int(i1)) => i0 == i1,
        (float(f0), float(f1)) => f0 == f1,
        (bool(b0), bool(b1)) => b0 == b1,
        (null, null) => true,
        (map(d0), map(d1)) => {
          if d0.size() == d1.size() {
              for d0.each() |k, v| {
                  if !d1.contains_key(k) || !eq(d1.get(k), v) {
                      return false;
                  }
              }
              true
          } else {
              false
          }
        }
        (vec(v0), vec(v1)) => vec::all2(*v0, *v1, eq),
        _ => false
    }
}

#[cfg(test)]
mod tests {
    // Tests inspired by https://github.com/rfk/tnetstring.

    fn test(string: ~str, expected: tnetstring) {
        let (actual, rest) = from_str(string);
        assert actual.is_some();
        assert rest == ~"";

        let actual = actual.get();
        assert eq(actual, expected);
        assert to_str(expected) == string;
    }

    #[test]
    fn test_format() {
        test(~"11:hello world,", str(@str::to_bytes("hello world")));
        test(~"0:}", map(map::bytes_hash()));
        test(~"0:]", vec(@~[]));

        let d = map::bytes_hash();
        d.insert(str::to_bytes("hello"),
                vec(@~[
                    int(12345678901),
                    str(@str::to_bytes("this")),
                    bool(true),
                    null,
                    str(@str::to_bytes("\x00\x00\x00\x00"))]));

        test(~"51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00\
               \x00,]}", map(d));

        test(~"5:12345#", int(12345));
        test(~"12:this is cool,", str(@str::to_bytes("this is cool")));
        test(~"0:,", str(@str::to_bytes("")));
        test(~"0:~", null);
        test(~"4:true!", bool(true));
        test(~"5:false!", bool(false));
        test(~"10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,",
            str(@str::to_bytes("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")));
        test(~"24:5:12345#5:67890#5:xxxxx,]",
            vec(@~[
                int(12345),
                int(67890),
                str(@str::to_bytes("xxxxx"))]));
        test(~"18:3:0.1^3:0.2^3:0.4^]",
           vec(@~[float(0.1), float(0.2), float(0.4)]));
        test(~"243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:\
               168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:\
               91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:\
               11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\
               ]]]]",
            vec(
                @~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(
                @~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(
                @~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(
                @~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(
                @~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(
                @~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(@~[vec(
                @~[vec(@~[vec(@~[
                    str(@str::to_bytes("hello-there"))
                ])])])])])])])])])])])])])])])])])])])])])])])])])])])])
                ])])])])])])])])])])])])])])])])])])])])])])]));
    }

    #[test]
    fn test_random() {
        fn randint(rng: rand::Rng, a: u32, b: u32) -> u32 {
            (rng.next() % (b - a + 1u32)) + a
        }

        fn get_random_object(rng: rand::Rng, depth: u32) -> tnetstring {
            if randint(rng, depth, 10u32) <= 4u32 {
                if randint(rng, 0u32, 1u32) == 0u32 {
                    let n = randint(rng, 0u32, 10u32);
                    vec(@vec::from_fn(n as uint, |_i|
                        get_random_object(rng, depth + 1u32)
                    ))
                } else {
                    let d = map::bytes_hash();

                    let mut i = randint(rng, 0u32, 10u32);
                    while i != 0u32 {
                        let s = rng.gen_bytes(randint(rng, 0u32, 100u32) as uint);
                        d.insert(s, get_random_object(rng, depth + 1u32));
                        i -= 1u32;
                    }
                    map(d)
                }
            } else {
                match randint(rng, 0u32, 5u32) {
                  0u32 => null,
                  1u32 => bool(true),
                  2u32 => bool(false),
                  3u32 => {
                    if randint(rng, 0u32, 1u32) == 0u32 {
                        int(rng.next() as int)
                    } else {
                        int(-rng.next() as int)
                    }
                  }
                  4u32 => {
                    let mut f = rng.gen_float();

                    // Generate a float that can be exactly converted to
                    // and from a string.
                    loop {
                        match float::from_str(float::to_str(f, 6u)) {
                          Some(f1) => {
                            if f == f1 { break; }
                            f = f1;
                          }
                          None => fail ~"invalid float"
                        }
                    }

                    if randint(rng, 0u32, 1u32) == 0u32 {
                        float(f)
                    } else {
                        float(-f)
                    }
                  }
                  5u32 => {
                    str(@rng.gen_bytes(randint(rng, 0u32, 100u32) as uint))
                  }
                  _ => fail
                }
            }
        }

        let rng = rand::Rng();

        let mut i = 500u;
        while i != 0u {
            let v0 = get_random_object(rng, 0u32);

            match from_bytes(to_bytes(v0)) {
                (Some(v1), rest) if rest == ~[] => assert eq(v0, v1),
                _ => fail ~"invalid tnetstring"
            }
            i -= 1u;
        }
    }
}
<commit_msg>Make tnetstrings sendable.<commit_after>/// Rust TNetStrings serialization library.

export tnetstring;
export str;
export int;
export float;
export bool;
export null;
export map;
export vec;
export to_writer;
export to_bytes;
export to_str;
export from_reader;
export from_bytes;
export from_str;

/// Represents a tnetstring value.
enum tnetstring {
    str(~[u8]),
    int(int),
    float(float),
    bool(bool),
    null,
    map(~send_map::linear::LinearMap<~[u8], tnetstring>),
    vec(~[tnetstring]),
}

/// Serializes a tnetstring value into a io::Writer.
fn to_writer(writer: io::Writer, tnetstring: tnetstring) {
    fn write_str(wr: io::Writer, s: &[u8]) {
        wr.write_str(#fmt("%u:", s.len()));
        wr.write(s);
        wr.write_char(',');
    }

    match tnetstring {
        str(s) => write_str(writer, s),
        int(i) => {
            let s = int::str(i);
            writer.write_str(#fmt("%u:%s#", s.len(), s));
        }
        float(f) => {
            let s = float::to_str(f, 6u);
            writer.write_str(#fmt("%u:%s^", s.len(), s));
        }
        bool(b) => {
            let s = bool::to_str(b);
            writer.write_str(#fmt("%u:%s!", s.len(), s));
        }
        map(m) => {
            let buf = io::mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            for m.each_ref |key, value| {
                write_str(wr, *key);
                to_writer(wr, *value);
            }
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", payload.len()));
            writer.write(payload);
            writer.write_char('}');
        }
        vec(v) => {
            let buf = io::mem_buffer();
            let wr = io::mem_buffer_writer(buf);
            for v.each |e| { to_writer(wr, e) }
            let payload = io::mem_buffer_buf(buf);
            writer.write_str(#fmt("%u:", payload.len()));
            writer.write(payload);
            writer.write_char(']');
        }
        null => writer.write_str("0:~"),
    }
}

/// Serializes a tnetstring value into a byte string.
fn to_bytes(tnetstring: tnetstring) -> ~[u8] {
    let buf = io::mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, tnetstring);
    io::mem_buffer_buf(buf)
}

/// Serializes a tnetstring value into a string.
fn to_str(tnetstring: tnetstring) -> ~str {
    let buf = io::mem_buffer();
    let wr = io::mem_buffer_writer(buf);
    to_writer(wr, tnetstring);
    io::mem_buffer_str(buf)
}

/// Deserializes a tnetstring value from an io::Reader.
fn from_reader(reader: io::Reader) -> Option<tnetstring> {
    assert !reader.eof();

    let mut c = reader.read_byte();
    let mut len = 0u;

    // Note that netstring spec explicitly forbids padding zeros.
    // If the first char is zero, it must be the only char.
    if c < '0' as int || c > '9' as int {
        fail ~"Not a tnetstring: invalid or missing length prefix";
    } else if c == '0' as int {
        c = reader.read_byte();
    } else {
        loop {
            len = (10u * len) + ((c as uint) - ('0' as uint));

            if reader.eof() {
                fail ~"Not a tnetstring: invalid or missing length prefix";
            }
            c = reader.read_byte();

            if c < '0' as int || c > '9' as int {
                break;
            }
        }
    }

    // Validate end-of-length-prefix marker.
    if c != ':' as int {
        fail ~"Not a tnetstring: missing length prefix";
    }

    // Read the data plus terminating type tag.
    let payload = reader.read_bytes(len);

    if payload.len() != len {
        fail ~"Not a tnetstring: invalid length prefix";
    }

    if reader.eof() {
        fail ~"Not a tnetstring: missing type tag";
    }

    match reader.read_byte() as char {
      '#' => {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        int::from_str(s).map(|v| int(v))
      }
      '}' => Some(map(parse_map(payload))),
      ']' => Some(vec(parse_vec(payload))),
      '!' => {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        bool::from_str(s).map(|v| bool(v))
      }
      '^' => {
        let s = unsafe { str::unsafe::from_bytes(payload) };
        float::from_str(s).map(|v| float(v))
      }
      '~' => {
        assert payload.len() == 0u;
        Some(null)
      }
      ',' => Some(str(payload)),
      c => {
        let s = str::from_char(c);
        fail #fmt("Invalid payload type: %?", s)
      }
    }
}

fn parse_vec(data: ~[u8]) -> ~[tnetstring] {
    if data.len() == 0u { return ~[]; }

    do io::with_bytes_reader(data) |reader| {
        let mut result = ~[];

        match move from_reader(reader) {
            Some(move value) => vec::push(result, value),
            None => fail ~"invalid value"
        }

        while !reader.eof() {
            match move from_reader(reader) {
                Some(move value) => vec::push(result, value),
                None => fail ~"invalid tnetstring"
            }
        }

        result
    }
}

fn parse_pair(reader: io::Reader) -> (~[u8], tnetstring) {
    match move from_reader(reader) {
        Some(str(move key)) => {
            match move from_reader(reader) {
                Some(move value) => (key, value),
                None => fail ~"invalid tnetstring",
            }
        }
        Some(_) => fail ~"Keys can only be strings.",
        None => fail ~"Invalid tnetstring",
    }
}

fn parse_map(data: ~[u8]) -> ~send_map::linear::LinearMap<~[u8], tnetstring> {
    let result = ~send_map::linear::LinearMap();

    if data.len() != 0u {
        do io::with_bytes_reader(data) |reader| {
            let (key, value) = parse_pair(reader);
            result.insert(key, value);

            while !reader.eof() {
                let (key, value) = parse_pair(reader);
                result.insert(key, value);
            }
        }
    }

    result
}

/// Deserializes a tnetstring value from a byte string.
fn from_bytes(data: ~[u8]) -> (Option<tnetstring>, ~[u8]) {
    do io::with_bytes_reader(data) |reader| {
        let tnetstring = from_reader(reader);
        (tnetstring, reader.read_whole_stream())
    }
}

/// Deserializes a tnetstring value from a string.
fn from_str(data: &str) -> (Option<tnetstring>, ~str) {
    do io::with_str_reader(data) |rdr| {
        let tnetstring = from_reader(rdr);
        let bytes = rdr.read_whole_stream();
        (tnetstring, str::from_bytes(bytes))
    }

}

/// Test the equality between two tnetstring values
fn eq(t0: &tnetstring, t1: &tnetstring) -> bool {
    // FIXME: https://github.com/mozilla/rust/issues/2855
    match (copy *t0, copy *t1) {
        (str(s0), str(s1)) => s0 == s1,
        (int(i0), int(i1)) => i0 == i1,
        (float(f0), float(f1)) => f0 == f1,
        (bool(b0), bool(b1)) => b0 == b1,
        (null, null) => true,
        (map(d0), map(d1)) => {
            if d0.len() == d1.len() {
                for d0.each_ref |k0, v0| {
                    let result = do d1.with_find_ref(k0) |v1| {
                        match v1 {
                            Some(v1) => eq(v0, v1),
                            None => false,
                        }
                    };
                    if !result { return false; }
                }
                true
            } else {
                false
            }
        }
        (vec(v0), vec(v1)) => vec::all2(v0, v1, |x0, x1| eq(&x0, &x1)),
        _ => false
    }
}

#[cfg(test)]
mod tests {
    // Tests inspired by https://github.com/rfk/tnetstring.

    fn test(string: ~str, expected: tnetstring) {
        let (actual, rest) = from_str(string);
        assert actual.is_some();
        assert rest == ~"";

        let actual = option::unwrap(actual);
        assert eq(&actual, &expected);
        assert to_str(expected) == string;
    }

    #[test]
    fn test_format() {
        test(~"11:hello world,", str(str::to_bytes("hello world")));
        test(~"0:}", map(~send_map::linear::LinearMap()));
        test(~"0:]", vec(~[]));

        let d = ~send_map::linear::LinearMap();
        d.insert(str::to_bytes("hello"),
                vec(~[
                    int(12345678901),
                    str(str::to_bytes("this")),
                    bool(true),
                    null,
                    str(str::to_bytes("\x00\x00\x00\x00"))]));

        test(~"51:5:hello,39:11:12345678901#4:this,4:true!0:~4:\x00\x00\x00\
               \x00,]}", map(d));

        test(~"5:12345#", int(12345));
        test(~"12:this is cool,", str(str::to_bytes("this is cool")));
        test(~"0:,", str(str::to_bytes("")));
        test(~"0:~", null);
        test(~"4:true!", bool(true));
        test(~"5:false!", bool(false));
        test(~"10:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,",
            str(str::to_bytes("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")));
        test(~"24:5:12345#5:67890#5:xxxxx,]",
            vec(~[
                int(12345),
                int(67890),
                str(str::to_bytes("xxxxx"))]));
        test(~"18:3:0.1^3:0.2^3:0.4^]",
           vec(~[float(0.1), float(0.2), float(0.4)]));
        test(~"243:238:233:228:223:218:213:208:203:198:193:188:183:178:173:\
               168:163:158:153:148:143:138:133:128:123:118:113:108:103:99:95:\
               91:87:83:79:75:71:67:63:59:55:51:47:43:39:35:31:27:23:19:15:\
               11:hello-there,]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\
               ]]]]",
            vec(
                ~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(
                ~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(
                ~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(
                ~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(
                ~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(
                ~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(~[vec(
                ~[vec(~[vec(~[
                    str(str::to_bytes("hello-there"))
                ])])])])])])])])])])])])])])])])])])])])])])])])])])])])
                ])])])])])])])])])])])])])])])])])])])])])])]));
    }

    #[test]
    fn test_random() {
        fn randint(rng: rand::Rng, a: u32, b: u32) -> u32 {
            (rng.next() % (b - a + 1u32)) + a
        }

        fn get_random_object(rng: rand::Rng, depth: u32) -> tnetstring {
            if randint(rng, depth, 10u32) <= 4u32 {
                if randint(rng, 0u32, 1u32) == 0u32 {
                    let n = randint(rng, 0u32, 10u32);
                    vec(vec::from_fn(n as uint, |_i|
                        get_random_object(rng, depth + 1u32)
                    ))
                } else {
                    let d = ~send_map::linear::LinearMap();

                    let mut i = randint(rng, 0u32, 10u32);
                    while i != 0u32 {
                        let s = rng.gen_bytes(randint(rng, 0u32, 100u32) as uint);
                        d.insert(s, get_random_object(rng, depth + 1u32));
                        i -= 1u32;
                    }
                    map(d)
                }
            } else {
                match randint(rng, 0u32, 5u32) {
                  0u32 => null,
                  1u32 => bool(true),
                  2u32 => bool(false),
                  3u32 => {
                    if randint(rng, 0u32, 1u32) == 0u32 {
                        int(rng.next() as int)
                    } else {
                        int(-rng.next() as int)
                    }
                  }
                  4u32 => {
                    let mut f = rng.gen_float();

                    // Generate a float that can be exactly converted to
                    // and from a string.
                    loop {
                        match float::from_str(float::to_str(f, 6u)) {
                          Some(f1) => {
                            if f == f1 { break; }
                            f = f1;
                          }
                          None => fail ~"invalid float"
                        }
                    }

                    if randint(rng, 0u32, 1u32) == 0u32 {
                        float(f)
                    } else {
                        float(-f)
                    }
                  }
                  5u32 => {
                    str(rng.gen_bytes(randint(rng, 0u32, 100u32) as uint))
                  }
                  _ => fail
                }
            }
        }

        let rng = rand::Rng();

        let mut i = 500u;
        while i != 0u {
            let v0 = get_random_object(rng, 0u32);

            match from_bytes(to_bytes(v0)) {
                (Some(v1), rest) if rest == ~[] => assert eq(&v0, &v1),
                _ => fail ~"invalid tnetstring"
            }
            i -= 1u;
        }
    }
}
<|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.

use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;

use instance::QueueFamily;

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

/// A pool from which command buffers are created from.
pub struct CommandBufferPool {
    pool: Mutex<vk::CommandPool>,
    device: Arc<Device>,
    queue_family_index: u32,
}

impl CommandBufferPool {
    /// Creates a new pool.
    ///
    /// The command buffers created with this pool can only be executed on queues of the given
    /// family.
    ///
    /// # Panic
    ///
    /// Panicks if the queue family doesn't belong to the same physical device as `device`.
    ///
    #[inline]
    pub fn new(device: &Arc<Device>, queue_family: &QueueFamily)
               -> Result<Arc<CommandBufferPool>, OomError>
    {
        assert_eq!(device.physical_device().internal_object(),
                   queue_family.physical_device().internal_object());

        let vk = device.pointers();

        let pool = unsafe {
            let infos = vk::CommandPoolCreateInfo {
                sType: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,       // TODO:
                queueFamilyIndex: queue_family.id(),
            };

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

        Ok(Arc::new(CommandBufferPool {
            pool: Mutex::new(pool),
            device: device.clone(),
            queue_family_index: queue_family.id(),
        }))
    }

    /// Returns the device this command pool was created with.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the queue family on which command buffers of this pool can be executed.
    #[inline]
    pub fn queue_family(&self) -> QueueFamily {
        self.device.physical_device().queue_family_by_id(self.queue_family_index).unwrap()
    }
}

unsafe impl SynchronizedVulkanObject for CommandBufferPool {
    type Object = vk::CommandPool;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::CommandPool> {
        self.pool.lock().unwrap()
    }
}

impl Drop for CommandBufferPool {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            let pool = self.pool.lock().unwrap();
            vk.DestroyCommandPool(self.device.internal_object(), *pool, ptr::null());
        }
    }
}
<commit_msg>optional oomerror for CommandBufferPool<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.

use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;

use instance::QueueFamily;

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

/// A pool from which command buffers are created from.
pub struct CommandBufferPool {
    pool: Mutex<vk::CommandPool>,
    device: Arc<Device>,
    queue_family_index: u32,
}

impl CommandBufferPool {
    /// Creates a new pool.
    ///
    /// The command buffers created with this pool can only be executed on queues of the given
    /// family.
    ///
    /// # Panic
    ///
    /// Panicks if the queue family doesn't belong to the same physical device as `device`.
    ///
    #[inline]
    pub fn raw(device: &Arc<Device>, queue_family: &QueueFamily)
               -> Result<CommandBufferPool, OomError>
    {
        assert_eq!(device.physical_device().internal_object(),
                   queue_family.physical_device().internal_object());

        let vk = device.pointers();

        let pool = unsafe {
            let infos = vk::CommandPoolCreateInfo {
                sType: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,       // TODO:
                queueFamilyIndex: queue_family.id(),
            };

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

        Ok(CommandBufferPool {
            pool: Mutex::new(pool),
            device: device.clone(),
            queue_family_index: queue_family.id(),
        })
    }

    /// Creates a new pool.
    ///
    /// The command buffers created with this pool can only be executed on queues of the given
    /// family.
    ///
    /// # Panic
    ///
    /// - Panicks if the queue family doesn't belong to the same physical device as `device`.
    /// - Panicks if the device or host ran out of memory.
    ///
    #[inline]
    pub fn new(device: &Arc<Device>, queue_family: &QueueFamily)
               -> Arc<CommandBufferPool>
    {
        Arc::new(CommandBufferPool::raw(device, queue_family).unwrap())
    }

    /// Returns the device this command pool was created with.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the queue family on which command buffers of this pool can be executed.
    #[inline]
    pub fn queue_family(&self) -> QueueFamily {
        self.device.physical_device().queue_family_by_id(self.queue_family_index).unwrap()
    }
}

unsafe impl SynchronizedVulkanObject for CommandBufferPool {
    type Object = vk::CommandPool;

    #[inline]
    fn internal_object_guard(&self) -> MutexGuard<vk::CommandPool> {
        self.pool.lock().unwrap()
    }
}

impl Drop for CommandBufferPool {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            let pool = self.pool.lock().unwrap();
            vk.DestroyCommandPool(self.device.internal_object(), *pool, ptr::null());
        }
    }
}
<|endoftext|>


<commit_before>///Lazy git tool
use std::env::args as cmd_args;
use std::process::{Command, Stdio};

///Executes git command with arguments
///Building sequence of arguments like .arg(@arg)
macro_rules! exec_git_cmd {
    (all_fd=>$fd:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($fd).stderr($fd).status().unwrap(); };
    (stdout=>$stdout:expr, stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).stderr($stderr).status().unwrap(); };
    (stdout=>$stdout:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).status().unwrap(); };
    (stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stderr($stderr).status().unwrap(); };
    ($($arg:expr),*) => { Command::new("git")$(.arg($arg))*.status().unwrap(); };
}

///Print with prefix >>>
macro_rules! trace {
    ($($arg:expr),*) => { println!(">>>{}", [$(format!("{}", $arg),)*].connect(" ")); }
}

#[inline(always)]
fn usage() {
    println!("lgit [option]\n");
    println!("options:");
    println!("  amend [edit] - amend all changes into last commit");
    println!("  add - add all changes");
    println!("  clean - undo all changes");
    println!("  push [force] - push current branch");
    println!("  commit [title] [--subj description] - commit with message(option subj place commit description)");
    println!("  fetch - get updates from upstream\n");
}

///Amend changes
fn git_amend() {
    git_add();
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 == "edit" { exec_git_cmd!("commit", "--amend"); }
        else { trace!("Incorrect amend argument:", arg3); }
    }
    else { exec_git_cmd!("commit", "--amend", "--no-edit"); }
}

///Fetch changes from upstream
fn git_fetch() {
    exec_git_cmd!("checkout", "master");
    exec_git_cmd!("fetch", "upstream");
    exec_git_cmd!("merge", "upstream/master");
}

///Push the current branch
fn git_push() {
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 =="force" { exec_git_cmd!("push", "--force", "origin", "HEAD"); }
        else { trace!("Incorrect push argument:", arg3); }
    }
    else { exec_git_cmd!("push", "origin", "HEAD"); }
}

///Add all changes
fn git_add() {
    exec_git_cmd!("add", "--all");
}

///Undo all changes
fn git_clean() {
    exec_git_cmd!("checkout", "-f");
    exec_git_cmd!("clean", "-fdq");
}

///Commit with message
fn git_commit() {
    if cmd_args().skip(2).len() == 0 {
        trace!("Empty commit message");
    }
    else {
        let args: Vec<String> = cmd_args().skip(2).collect();
        let mut args_split = args.split(|elem| elem.to_lowercase() == "--subj");

        //If no --subj split will have only one element.
        //Consider this case as the whole message.
        let title: String = args_split.next().unwrap().connect(" ");
        if let Some(subj_array) = args_split.next() {
            let mut subj: String = subj_array.connect(" ").replace("\\n", "\n").lines().fold(String::new(), |acc, line| acc + line.trim() + "\n");
            subj.pop();
            exec_git_cmd!("commit", "-m", format!("{}\n\n{}", title, subj));
        }
        else {
            let mut title = title.replace("\\n", "\n").lines().fold(String::new(), |acc, line| acc + line.trim() + "\n");
            title.pop();
            exec_git_cmd!("commit", "-m", title);
        }

    }
}

///Unexpected argument handler
fn unexpected(arg: &str) {
    trace!("Unexpected argument:", arg);
    usage();
}

fn main() {
    //To check if git repo present
    if !exec_git_cmd!(stderr=>Stdio::null(), "rev-parse", "-q").success() {
        trace!("Not a git repository");
        return;
    }
    else if cmd_args().len() < 2 {
        usage();
        return;
    }

    match cmd_args().skip(1).next().unwrap().as_ref() {
        "amend"  => git_amend(),
        "fetch"  => git_fetch(),
        "push"   => git_push(),
        "add"    => git_add(),
        "clean"  => git_clean(),
        "commit" => git_commit(),
        arg @ _  => unexpected(&arg),
    }
}
<commit_msg>Rust: lgit update usage<commit_after>///Lazy git tool
use std::env::args as cmd_args;
use std::process::{Command, Stdio};

///Executes git command with arguments
///Building sequence of arguments like .arg(@arg)
macro_rules! exec_git_cmd {
    (all_fd=>$fd:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($fd).stderr($fd).status().unwrap(); };
    (stdout=>$stdout:expr, stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).stderr($stderr).status().unwrap(); };
    (stdout=>$stdout:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stdout($stdout).status().unwrap(); };
    (stderr=>$stderr:expr, $($arg:expr),*) => { Command::new("git")$(.arg($arg))*.stderr($stderr).status().unwrap(); };
    ($($arg:expr),*) => { Command::new("git")$(.arg($arg))*.status().unwrap(); };
}

///Print with prefix >>>
macro_rules! trace {
    ($($arg:expr),*) => { println!(">>>{}", [$(format!("{}", $arg),)*].connect(" ")); }
}

#[inline(always)]
fn usage() {
println!("usage: lgit [option]

options:
    amend [edit]                        - amend all changes into last commit
    add                                 - add all changes
    clean                               - undo all changes
    push [force]                        - push current branch
    commit [title] [--subj description] - commit with message(option subj place commit description)
    fetch                               - get updates from upstream
");
}

///Amend changes
fn git_amend() {
    git_add();
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 == "edit" { exec_git_cmd!("commit", "--amend"); }
        else { trace!("Incorrect amend argument:", arg3); }
    }
    else { exec_git_cmd!("commit", "--amend", "--no-edit"); }
}

///Fetch changes from upstream
fn git_fetch() {
    exec_git_cmd!("checkout", "master");
    exec_git_cmd!("fetch", "upstream");
    exec_git_cmd!("merge", "upstream/master");
}

///Push the current branch
fn git_push() {
    if let Some(arg3) = cmd_args().skip(2).next() {
        if arg3 =="force" { exec_git_cmd!("push", "--force", "origin", "HEAD"); }
        else { trace!("Incorrect push argument:", arg3); }
    }
    else { exec_git_cmd!("push", "origin", "HEAD"); }
}

///Add all changes
fn git_add() {
    exec_git_cmd!("add", "--all");
}

///Undo all changes
fn git_clean() {
    exec_git_cmd!("checkout", "-f");
    exec_git_cmd!("clean", "-fdq");
}

///Commit with message
fn git_commit() {
    if cmd_args().skip(2).len() == 0 {
        trace!("Empty commit message");
    }
    else {
        let args: Vec<String> = cmd_args().skip(2).collect();
        let mut args_split = args.split(|elem| elem.to_lowercase() == "--subj");

        //If no --subj split will have only one element.
        //Consider this case as the whole message.
        let title: String = args_split.next().unwrap().connect(" ");
        if let Some(subj_array) = args_split.next() {
            let mut subj: String = subj_array.connect(" ").replace("\\n", "\n").lines().fold(String::new(), |acc, line| acc + line.trim() + "\n");
            subj.pop();
            exec_git_cmd!("commit", "-m", format!("{}\n\n{}", title, subj));
        }
        else {
            let mut title = title.replace("\\n", "\n").lines().fold(String::new(), |acc, line| acc + line.trim() + "\n");
            title.pop();
            exec_git_cmd!("commit", "-m", title);
        }

    }
}

///Unexpected argument handler
fn unexpected(arg: &str) {
    trace!("Unexpected argument:", arg);
    usage();
}

fn main() {
    //To check if git repo present
    if !exec_git_cmd!(stderr=>Stdio::null(), "rev-parse", "-q").success() {
        trace!("Not a git repository");
        return;
    }
    else if cmd_args().len() < 2 {
        usage();
        return;
    }

    match cmd_args().skip(1).next().unwrap().as_ref() {
        "amend"  => git_amend(),
        "fetch"  => git_fetch(),
        "push"   => git_push(),
        "add"    => git_add(),
        "clean"  => git_clean(),
        "commit" => git_commit(),
        arg @ _  => unexpected(&arg),
    }
}
<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use objc::runtime::{BOOL, NO, YES};
use sys::{MTLCreateSystemDefaultDevice, MTLCopyAllDevices, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;
use {Size};

pub trait ReadOnlyDevice {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool;
    fn is_headless(&self) -> bool;
    fn is_low_power(&self) -> bool;
    fn max_threads_per_group(&self) -> Size;
    fn get_name(&self) -> Cow<str>;
    fn supports_texture_sample_count(&self, sample_count: usize) -> bool;
}

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    #[allow(unused_mut, unused_variables)]
    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        devices_vec
    }

    /// Get the underlying pointer to the device. Releases ownership.
    pub unsafe fn into_raw(self) -> id {
        self.0
    }

    /// Create a `Device` from a raw pointer. Does no error checking, so it
    /// will cause errors if the device is nil, or does not conform to the
    /// MTLDevice protocol.
    pub unsafe fn from_raw_unchecked(device_ptr: id) -> Self {
        Device(device_ptr)
    }

    /// Create a `Device` from a raw pointer. If the pointer is not nil and
    /// conforms to the `MTLDevice` protocol, then a `Device` will be created,
    /// otherwise returns a `DeviceError`.
    pub fn from_raw(device_ptr: id) -> Result<Self, DeviceError> {
        #[link(name = "Foundation", kind = "framework")]
        extern {
            fn NSProtocolFromString(namestr: id) -> id;
        }

        let conforms_to_protocol: BOOL = unsafe {
            let mtl_device_protocol_str = NSString::alloc(nil).init_str("MTLDevice");
            let mtl_device_protocol = NSProtocolFromString(mtl_device_protocol_str);
            msg_send![device_ptr, conformsToProtocol:mtl_device_protocol]
        };

        if device_ptr == nil {
            Err(DeviceError::ConstructedFromNil)
        } else if conforms_to_protocol == NO {
            Err(DeviceError::ConstructedFromWrongPointerType)
        } else {
            Ok(Device(device_ptr))
        }
    }
}

impl ReadOnlyDevice for Device {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to get a Device's id without consuming it.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _device_get_raw(device: &Device) -> id {
    device.0
}

// TODO(George): Should this have a lifetime associated with it?
pub struct DeviceRef(id);

impl DeviceRef {
    pub fn is_reference_to(&self, device: &Device) -> bool {
        self.0 == device.0
    }
}

impl ReadOnlyDevice for DeviceRef {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to create a DeviceRef.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _make_device_ref(device: id) -> DeviceRef {
    DeviceRef(device)
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    ConstructedFromNil,
    ConstructedFromWrongPointerType,
    OtherRuntimeError
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::ConstructedFromNil => "DeviceError::ConstructedFromNil",
            DeviceError::ConstructedFromWrongPointerType => "DeviceError::ConstructedFromWrongPointerType",
            DeviceError::OtherRuntimeError => "DeviceError::OtherRuntimeError"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0",
            DeviceError::ConstructedFromNil => "Attempted to create a device from a nil pointer",
            DeviceError::ConstructedFromWrongPointerType =>
                "Attempted to create a device from a pointer which does not implement MTLDevice",
            DeviceError::OtherRuntimeError => "An error occured in the Objective-C runtime"
        }
    }
}
<commit_msg>Alphebetise imports<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use objc::runtime::{BOOL, NO, YES};
use sys::{MTLCreateSystemDefaultDevice, MTLCopyAllDevices, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use {Size};

pub trait ReadOnlyDevice {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool;
    fn is_headless(&self) -> bool;
    fn is_low_power(&self) -> bool;
    fn max_threads_per_group(&self) -> Size;
    fn get_name(&self) -> Cow<str>;
    fn supports_texture_sample_count(&self, sample_count: usize) -> bool;
}

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    #[allow(unused_mut, unused_variables)]
    pub fn enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        devices_vec
    }

    /// Get the underlying pointer to the device. Releases ownership.
    pub unsafe fn into_raw(self) -> id {
        self.0
    }

    /// Create a `Device` from a raw pointer. Does no error checking, so it
    /// will cause errors if the device is nil, or does not conform to the
    /// MTLDevice protocol.
    pub unsafe fn from_raw_unchecked(device_ptr: id) -> Self {
        Device(device_ptr)
    }

    /// Create a `Device` from a raw pointer. If the pointer is not nil and
    /// conforms to the `MTLDevice` protocol, then a `Device` will be created,
    /// otherwise returns a `DeviceError`.
    pub fn from_raw(device_ptr: id) -> Result<Self, DeviceError> {
        #[link(name = "Foundation", kind = "framework")]
        extern {
            fn NSProtocolFromString(namestr: id) -> id;
        }

        let conforms_to_protocol: BOOL = unsafe {
            let mtl_device_protocol_str = NSString::alloc(nil).init_str("MTLDevice");
            let mtl_device_protocol = NSProtocolFromString(mtl_device_protocol_str);
            msg_send![device_ptr, conformsToProtocol:mtl_device_protocol]
        };

        if device_ptr == nil {
            Err(DeviceError::ConstructedFromNil)
        } else if conforms_to_protocol == NO {
            Err(DeviceError::ConstructedFromWrongPointerType)
        } else {
            Ok(Device(device_ptr))
        }
    }
}

impl ReadOnlyDevice for Device {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to get a Device's id without consuming it.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _device_get_raw(device: &Device) -> id {
    device.0
}

// TODO(George): Should this have a lifetime associated with it?
pub struct DeviceRef(id);

impl DeviceRef {
    pub fn is_reference_to(&self, device: &Device) -> bool {
        self.0 == device.0
    }
}

impl ReadOnlyDevice for DeviceRef {
    fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

    fn is_headless(&self) -> bool {
        unsafe { self.0.headless() == YES }
    }

    fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() == YES }
    }

    fn max_threads_per_group(&self) -> Size {
        unsafe { self.0.maxThreadsPerGroup().into() }
    }

    fn get_name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) == YES }
    }
}

/// Internal utility function to create a DeviceRef.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub unsafe fn _make_device_ref(device: id) -> DeviceRef {
    DeviceRef(device)
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed,
    ConstructedFromNil,
    ConstructedFromWrongPointerType,
    OtherRuntimeError
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::ConstructedFromNil => "DeviceError::ConstructedFromNil",
            DeviceError::ConstructedFromWrongPointerType => "DeviceError::ConstructedFromWrongPointerType",
            DeviceError::OtherRuntimeError => "DeviceError::OtherRuntimeError"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0",
            DeviceError::ConstructedFromNil => "Attempted to create a device from a nil pointer",
            DeviceError::ConstructedFromWrongPointerType =>
                "Attempted to create a device from a pointer which does not implement MTLDevice",
            DeviceError::OtherRuntimeError => "An error occured in the Objective-C runtime"
        }
    }
}
<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use sys::{MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() != 0 }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() != 0 }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()) }.to_string_lossy()
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0"
        }
    }
}
<commit_msg>Implement Device::supports_texture_sample_count<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSUInteger, NSString};
use sys::{MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::ffi::CStr;

pub struct Device(id);

impl Device {
    pub fn system_default_device() -> Result<Self, DeviceError> {
        let device = unsafe { MTLCreateSystemDefaultDevice() };
        if device != nil {
            Ok(Device(device))
        } else {
            Err(DeviceError::ConstructionFailed)
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe { self.0.headless() != 0 }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe { self.0.lowPower() != 0 }
    }

    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()) }.to_string_lossy()
    }

    pub fn supports_texture_sample_count(&self, sample_count: usize) -> bool {
        unsafe { self.0.supportsTextureSampleCount(sample_count as NSUInteger) != 0 }
    }
}

#[derive(Clone, Debug)]
pub enum DeviceError {
    ConstructionFailed
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed"
        };
        write!(f, "{}", descr)
    }
}

impl Error for DeviceError {
    fn description(&self) -> &str {
        match *self {
            DeviceError::ConstructionFailed =>
                "Could not create a default device. Please ensure that you are using at least OSX 10.11 or iOS 8.0"
        }
    }
}
<|endoftext|>