<commit_before>//! 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 std::marker::PhantomData;
use rustc_serialize::json;
use common::*;
use auth;
use error;
use error::Error::InvalidResponse;
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())));
        }

        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<i64>,
}

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())));
        }

        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 = i64;

    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 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 con_token = egg_mode::Token::new("", "");
///# let access_token = egg_mode::Token::new("", "");
///for name in egg_mode::user::followers_of("rustlang", &con_token, &access_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 con_token = egg_mode::Token::new("", "");
///# let access_token = egg_mode::Token::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", &con_token, &access_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 con_token = egg_mode::Token::new("", "");
///# let access_token = egg_mode::Token::new("", "");
///let mut list = egg_mode::user::followers_of("rustlang", &con_token, &access_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
{
    link: &'static str,
    con_token: &'a auth::Token<'a>,
    access_token: &'a auth::Token<'a>,
    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,
    iter: Option<ResponseIter<T::Item>>,
    _marker: PhantomData<T>,
}

impl<'a, T> CursorIter<'a, T>
    where T: Cursor + FromJson
{
    ///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,
                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) -> WebResponse<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 mut resp = try!(auth::get(self.link, self.con_token, self.access_token, Some(&params)));

        parse_response(&mut resp)
    }

    ///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.
    pub fn new(link: &'static str, con_token: &'a auth::Token, access_token: &'a auth::Token,
               params_base: Option<ParamList<'a>>, page_size: Option<i32>) -> CursorIter<'a, T> {
        CursorIter {
            link: link,
            con_token: con_token,
            access_token: access_token,
            params_base: params_base,
            page_size: page_size,
            previous_cursor: -1,
            next_cursor: -1,
            iter: None,
            _marker: PhantomData,
        }
    }
}

impl<'a, T> Iterator for CursorIter<'a, T>
    where T: Cursor + FromJson
{
    type Item = WebResponse<T::Item>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(ref mut results) = self.iter {
            if let Some(item) = results.next() {
                return Some(Ok(item));
            }
            else if self.next_cursor == 0 {
                return None;
            }
        }

        match self.call() {
            Ok(resp) => {
                self.previous_cursor = resp.response.previous_cursor_id();
                self.next_cursor = resp.response.next_cursor_id();

                let resp = Response {
                    rate_limit: resp.rate_limit,
                    rate_limit_remaining: resp.rate_limit_remaining,
                    rate_limit_reset: resp.rate_limit_reset,
                    response: resp.response.into_inner(),
                };

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

                match first {
                    Some(item) => Some(Ok(item)),
                    None => None,
                }
            },
            Err(err) => Some(Err(err)),
        }
    }
}
<commit_msg>hide docs for CursorIter::new<commit_after>//! 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 std::marker::PhantomData;
use rustc_serialize::json;
use common::*;
use auth;
use error;
use error::Error::InvalidResponse;
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())));
        }

        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<i64>,
}

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())));
        }

        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 = i64;

    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 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 con_token = egg_mode::Token::new("", "");
///# let access_token = egg_mode::Token::new("", "");
///for name in egg_mode::user::followers_of("rustlang", &con_token, &access_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 con_token = egg_mode::Token::new("", "");
///# let access_token = egg_mode::Token::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", &con_token, &access_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 con_token = egg_mode::Token::new("", "");
///# let access_token = egg_mode::Token::new("", "");
///let mut list = egg_mode::user::followers_of("rustlang", &con_token, &access_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
{
    link: &'static str,
    con_token: &'a auth::Token<'a>,
    access_token: &'a auth::Token<'a>,
    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,
    iter: Option<ResponseIter<T::Item>>,
    _marker: PhantomData<T>,
}

impl<'a, T> CursorIter<'a, T>
    where T: Cursor + FromJson
{
    ///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,
                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) -> WebResponse<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 mut resp = try!(auth::get(self.link, self.con_token, self.access_token, Some(&params)));

        parse_response(&mut resp)
    }

    ///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, con_token: &'a auth::Token, access_token: &'a auth::Token,
               params_base: Option<ParamList<'a>>, page_size: Option<i32>) -> CursorIter<'a, T> {
        CursorIter {
            link: link,
            con_token: con_token,
            access_token: access_token,
            params_base: params_base,
            page_size: page_size,
            previous_cursor: -1,
            next_cursor: -1,
            iter: None,
            _marker: PhantomData,
        }
    }
}

impl<'a, T> Iterator for CursorIter<'a, T>
    where T: Cursor + FromJson
{
    type Item = WebResponse<T::Item>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(ref mut results) = self.iter {
            if let Some(item) = results.next() {
                return Some(Ok(item));
            }
            else if self.next_cursor == 0 {
                return None;
            }
        }

        match self.call() {
            Ok(resp) => {
                self.previous_cursor = resp.response.previous_cursor_id();
                self.next_cursor = resp.response.next_cursor_id();

                let resp = Response {
                    rate_limit: resp.rate_limit,
                    rate_limit_remaining: resp.rate_limit_remaining,
                    rate_limit_reset: resp.rate_limit_reset,
                    response: resp.response.into_inner(),
                };

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

                match first {
                    Some(item) => Some(Ok(item)),
                    None => None,
                }
            },
            Err(err) => Some(Err(err)),
        }
    }
}
<|endoftext|>


<commit_before>use common::errors::{ChainErr, Result};
use common::file_utils::PathBufWithAdded;
use common::log;

use common::utils::MapIfOk;
use config::Config;
use cpp_checker::cpp_checker_step;
use cpp_ffi_generator::cpp_ffi_generator_step;
use cpp_parser::cpp_parser_step;

use common::string_utils::JoinWithSeparator;
use cpp_explicit_destructors::add_explicit_destructors_step;
use cpp_template_instantiator::find_template_instantiations_step;
use cpp_template_instantiator::instantiate_templates_step;
use database::{Database, DatabaseItem};
use html_logger::HtmlLogger;
use std::fmt;
use std::iter::once;
use std::path::PathBuf;
use type_allocation_places::choose_allocation_places_step;
use workspace::Workspace;
//use cpp_post_processor::cpp_post_process;

/// Creates output and cache directories if they don't exist.
/// Returns `Err` if any path in `config` is invalid or relative.
fn check_all_paths(config: &Config) -> Result<()> {
    let check_dir = |path: &PathBuf| -> Result<()> {
        if !path.is_absolute() {
            return Err(format!(
                "Only absolute paths allowed. Relative path: {}",
                path.display()
            )
            .into());
        }
        if !path.exists() {
            return Err(format!("Directory doesn't exist: {}", path.display()).into());
        }
        if !path.is_dir() {
            return Err(format!("Path is not a directory: {}", path.display()).into());
        }
        Ok(())
    };

    if let Some(path) = config.crate_template_path() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().include_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().lib_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().framework_paths() {
        check_dir(path)?;
    }
    for path in config.target_include_paths() {
        check_dir(path)?;
    }
    Ok(())
}

pub struct ProcessorData<'a> {
    pub workspace: &'a mut Workspace,
    pub config: &'a Config,
    pub current_database: &'a mut Database,
    pub dep_databases: &'a [Database],
    pub html_logger: HtmlLogger,
}

impl<'a> ProcessorData<'a> {
    pub fn all_databases(&self) -> Vec<&Database> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .collect()
    }
    pub fn all_items(&self) -> Vec<&DatabaseItem> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .flat_map(|d| d.items.iter())
            .collect()
    }
}

#[derive(Debug)]
pub struct ProcessorMainCycleItem {
    pub item_name: String,
    pub run_after: Vec<String>,
}

pub struct ProcessingStep {
    pub name: String,
    pub main_cycle_items: Vec<ProcessorMainCycleItem>,
    pub function: Box<Fn(ProcessorData) -> Result<()>>,
}

impl fmt::Debug for ProcessingStep {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ProcessingStep")
            .field("name", &self.name)
            .field("main_cycle_items", &self.main_cycle_items)
            .finish()
    }
}

impl ProcessingStep {
    pub fn new_custom<S: Into<String>, F: 'static + Fn(ProcessorData) -> Result<()>>(
        name: S,
        function: F,
    ) -> ProcessingStep {
        ProcessingStep {
            name: name.into(),
            function: Box::new(function),
            main_cycle_items: Vec::new(),
        }
    }
    pub fn new<S: Into<String>, F: 'static + Fn(ProcessorData) -> Result<()>>(
        name: S,
        run_after: Vec<String>,
        function: F,
    ) -> ProcessingStep {
        let name = name.into();
        ProcessingStep {
            name: name.clone(),
            function: Box::new(function),
            main_cycle_items: vec![ProcessorMainCycleItem {
                item_name: name,
                run_after,
            }],
        }
    }
}

mod steps {
    use common::string_utils::JoinWithSeparator;
    use database::CppCheckerInfo;
    use html_logger::escape_html;
    use processor::ProcessingStep;

    pub fn print_database() -> ProcessingStep {
        ProcessingStep::new_custom("print_database", |mut data| {
            data.html_logger.add_header(&["Item", "Environments"])?;

            for item in &data.current_database.items {
                data.html_logger.add(
                    &[
                        escape_html(&item.cpp_data.to_string()),
                        format!("{:?}", item.source),
                    ],
                    "database_item",
                )?;
                if let Some(ref ffi_items) = item.ffi_items {
                    for ffi_item in ffi_items {
                        let item_text = format!("{:?}", ffi_item.cpp_item);
                        let item_texts = ffi_item.checks.items.iter().map(|item| {
                            format!(
                                "
{}: {}
",
                                item.env.short_text(),
                                CppCheckerInfo::error_to_log(&item.error)
                            )
                        });
                        let env_text = format!("
{}
", item_texts.join(""));
                        data.html_logger
                            .add(&[escape_html(&item_text), env_text], "ffi_item")?;
                    }
                }
            }
            Ok(())
        })
    }
    pub fn clear() -> ProcessingStep {
        ProcessingStep::new_custom("clear", |data| {
            data.current_database.clear();
            Ok(())
        })
    }
    pub fn clear_ffi() -> ProcessingStep {
        ProcessingStep::new_custom("clear_ffi", |data| {
            for item in &mut data.current_database.items {
                item.ffi_items = None;
            }
            data.current_database.next_ffi_id = 0;
            Ok(())
        })
    }
}

pub fn process(workspace: &mut Workspace, config: &Config, operations: &[String]) -> Result<()> {
    log::status(format!(
        "Processing crate: {}",
        config.crate_properties().name()
    ));
    check_all_paths(&config)?;

    let standard_processing_steps = vec![
        cpp_parser_step(),
        add_explicit_destructors_step(),
        choose_allocation_places_step(),
        find_template_instantiations_step(),
        instantiate_templates_step(),
        cpp_ffi_generator_step(),
        // TODO: generate_slot_wrappers
        cpp_checker_step(),
        steps::print_database(),
        steps::clear_ffi(),
        steps::clear(),
    ];
    let all_processing_steps: Vec<_> = standard_processing_steps
        .iter()
        .chain(config.custom_processing_steps().iter())
        .collect();

    // TODO: allow to remove any prefix through `Config` (#25)
    #[allow(unused_variables)]
    let remove_qt_prefix = config.crate_properties().name().starts_with("qt_");

    log::status("Loading current crate data");
    let mut current_database = workspace
        .load_or_create_crate(config.crate_properties().name())
        .chain_err(|| "failed to load current crate data")?;

    if !config.dependent_cpp_crates().is_empty() {
        log::status("Loading dependencies");
    }
    let dependent_cpp_crates =
        config
            .dependent_cpp_crates()
            .iter()
            .map_if_ok(|name| -> Result<_> {
                workspace
                    .load_crate(name)
                    .chain_err(|| "failed to load dependency")
            })?;

    for operation in operations {
        if let Some(item) = all_processing_steps
            .iter()
            .find(|item| &item.name == operation)
        {
            log::status(format!("Running processor item: {}", &item.name));

            let html_logger = HtmlLogger::new(
                workspace
                    .log_path()?
                    .with_added(format!("{}_log.html", operation)),
                &format!("{} log", operation),
            )?;

            let data = ProcessorData {
                workspace,
                html_logger,
                current_database: &mut current_database,
                dep_databases: &dependent_cpp_crates,
                config,
            };
            (item.function)(data)?;
        } else {
            println!(
                "Unknown operation: {}. Supported operations: {}",
                operation,
                all_processing_steps
                    .iter()
                    .map(|item| &item.name)
                    .join(", ")
            );
        }
    }

    /*
    if exec_config.write_dependencies_local_paths {
    log::status(
    "Output Cargo.toml file will contain local paths of used dependencies \
      (use --no-local-paths to disable).",
    );
    } else {
    log::status(
    "Local paths will not be written to the output crate. Make sure all dependencies \
      are published before trying to compile the crate.",
    );
    }

    */

    /*
    parser_cpp_data.detect_signals_and_slots(
      &dependent_cpp_crates,
    )?;
    // TODO: rename `cpp_data_filters` to `parser_cpp_data_filters`
    if config.has_cpp_data_filters() {
      log::status("Running custom filters for C++ parser data");
      for filter in config.cpp_data_filters() {
        filter(&mut parser_cpp_data).chain_err(
          || "cpp_data_filter failed",
        )?;
      }
    }

    log::status("Post-processing parse result");
    let r = cpp_post_process(
      parser_cpp_data,
      dependent_cpp_crates,
      config.type_allocation_places(),
    )?;

    //...

    */

    for database in dependent_cpp_crates {
        workspace.put_crate(database, true);
    }
    let current_database_saved = !operations.iter().any(|op| op != "print_database");
    if !current_database_saved {
        log::status("Saving data");
    }
    workspace.put_crate(current_database, current_database_saved);
    workspace.save_data()?;
    Ok(())
}
<commit_msg>fix unchanged database detection<commit_after>use common::errors::{ChainErr, Result};
use common::file_utils::PathBufWithAdded;
use common::log;

use common::utils::MapIfOk;
use config::Config;
use cpp_checker::cpp_checker_step;
use cpp_ffi_generator::cpp_ffi_generator_step;
use cpp_parser::cpp_parser_step;

use common::string_utils::JoinWithSeparator;
use cpp_explicit_destructors::add_explicit_destructors_step;
use cpp_template_instantiator::find_template_instantiations_step;
use cpp_template_instantiator::instantiate_templates_step;
use database::{Database, DatabaseItem};
use html_logger::HtmlLogger;
use std::fmt;
use std::iter::once;
use std::path::PathBuf;
use type_allocation_places::choose_allocation_places_step;
use workspace::Workspace;
//use cpp_post_processor::cpp_post_process;

/// Creates output and cache directories if they don't exist.
/// Returns `Err` if any path in `config` is invalid or relative.
fn check_all_paths(config: &Config) -> Result<()> {
    let check_dir = |path: &PathBuf| -> Result<()> {
        if !path.is_absolute() {
            return Err(format!(
                "Only absolute paths allowed. Relative path: {}",
                path.display()
            )
            .into());
        }
        if !path.exists() {
            return Err(format!("Directory doesn't exist: {}", path.display()).into());
        }
        if !path.is_dir() {
            return Err(format!("Path is not a directory: {}", path.display()).into());
        }
        Ok(())
    };

    if let Some(path) = config.crate_template_path() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().include_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().lib_paths() {
        check_dir(path)?;
    }
    for path in config.cpp_build_paths().framework_paths() {
        check_dir(path)?;
    }
    for path in config.target_include_paths() {
        check_dir(path)?;
    }
    Ok(())
}

pub struct ProcessorData<'a> {
    pub workspace: &'a mut Workspace,
    pub config: &'a Config,
    pub current_database: &'a mut Database,
    pub dep_databases: &'a [Database],
    pub html_logger: HtmlLogger,
}

impl<'a> ProcessorData<'a> {
    pub fn all_databases(&self) -> Vec<&Database> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .collect()
    }
    pub fn all_items(&self) -> Vec<&DatabaseItem> {
        once(&self.current_database as &_)
            .chain(self.dep_databases.iter())
            .flat_map(|d| d.items.iter())
            .collect()
    }
}

#[derive(Debug)]
pub struct ProcessorMainCycleItem {
    pub item_name: String,
    pub run_after: Vec<String>,
}

pub struct ProcessingStep {
    pub name: String,
    pub is_const: bool,
    pub main_cycle_items: Vec<ProcessorMainCycleItem>,
    pub function: Box<Fn(ProcessorData) -> Result<()>>,
}

impl fmt::Debug for ProcessingStep {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ProcessingStep")
            .field("name", &self.name)
            .field("main_cycle_items", &self.main_cycle_items)
            .finish()
    }
}

impl ProcessingStep {
    pub fn new_custom<S: Into<String>, F: 'static + Fn(ProcessorData) -> Result<()>>(
        name: S,
        function: F,
    ) -> ProcessingStep {
        ProcessingStep {
            name: name.into(),
            is_const: false,
            function: Box::new(function),
            main_cycle_items: Vec::new(),
        }
    }
    pub fn new<S: Into<String>, F: 'static + Fn(ProcessorData) -> Result<()>>(
        name: S,
        run_after: Vec<String>,
        function: F,
    ) -> ProcessingStep {
        let name = name.into();
        ProcessingStep {
            name: name.clone(),
            is_const: false,
            function: Box::new(function),
            main_cycle_items: vec![ProcessorMainCycleItem {
                item_name: name,
                run_after,
            }],
        }
    }
}

mod steps {
    use common::string_utils::JoinWithSeparator;
    use database::CppCheckerInfo;
    use html_logger::escape_html;
    use processor::ProcessingStep;

    pub fn print_database() -> ProcessingStep {
        ProcessingStep {
            is_const: true,
            ..ProcessingStep::new_custom("print_database", |mut data| {
                data.html_logger.add_header(&["Item", "Environments"])?;

                for item in &data.current_database.items {
                    data.html_logger.add(
                        &[
                            escape_html(&item.cpp_data.to_string()),
                            format!("{:?}", item.source),
                        ],
                        "database_item",
                    )?;
                    if let Some(ref ffi_items) = item.ffi_items {
                        for ffi_item in ffi_items {
                            let item_text = format!("{:?}", ffi_item.cpp_item);
                            let item_texts = ffi_item.checks.items.iter().map(|item| {
                                format!(
                                    "
{}: {}
",
                                    item.env.short_text(),
                                    CppCheckerInfo::error_to_log(&item.error)
                                )
                            });
                            let env_text = format!("
{}
", item_texts.join(""));
                            data.html_logger
                                .add(&[escape_html(&item_text), env_text], "ffi_item")?;
                        }
                    }
                }
                Ok(())
            })
        }
    }
    pub fn clear() -> ProcessingStep {
        ProcessingStep::new_custom("clear", |data| {
            data.current_database.clear();
            Ok(())
        })
    }
    pub fn clear_ffi() -> ProcessingStep {
        ProcessingStep::new_custom("clear_ffi", |data| {
            for item in &mut data.current_database.items {
                item.ffi_items = None;
            }
            data.current_database.next_ffi_id = 0;
            Ok(())
        })
    }
}

pub fn process(workspace: &mut Workspace, config: &Config, operations: &[String]) -> Result<()> {
    log::status(format!(
        "Processing crate: {}",
        config.crate_properties().name()
    ));
    check_all_paths(&config)?;

    let standard_processing_steps = vec![
        cpp_parser_step(),
        add_explicit_destructors_step(),
        choose_allocation_places_step(),
        find_template_instantiations_step(),
        instantiate_templates_step(),
        cpp_ffi_generator_step(),
        // TODO: generate_slot_wrappers
        cpp_checker_step(),
        steps::print_database(),
        steps::clear_ffi(),
        steps::clear(),
    ];
    let all_processing_steps: Vec<_> = standard_processing_steps
        .iter()
        .chain(config.custom_processing_steps().iter())
        .collect();

    // TODO: allow to remove any prefix through `Config` (#25)
    #[allow(unused_variables)]
    let remove_qt_prefix = config.crate_properties().name().starts_with("qt_");

    log::status("Loading current crate data");
    let mut current_database = workspace
        .load_or_create_crate(config.crate_properties().name())
        .chain_err(|| "failed to load current crate data")?;

    if !config.dependent_cpp_crates().is_empty() {
        log::status("Loading dependencies");
    }
    let dependent_cpp_crates =
        config
            .dependent_cpp_crates()
            .iter()
            .map_if_ok(|name| -> Result<_> {
                workspace
                    .load_crate(name)
                    .chain_err(|| "failed to load dependency")
            })?;

    let mut current_database_saved = true;

    for operation in operations {
        if let Some(item) = all_processing_steps
            .iter()
            .find(|item| &item.name == operation)
        {
            log::status(format!("Running processor item: {}", &item.name));

            let html_logger = HtmlLogger::new(
                workspace
                    .log_path()?
                    .with_added(format!("{}_log.html", operation)),
                &format!("{} log", operation),
            )?;

            let data = ProcessorData {
                workspace,
                html_logger,
                current_database: &mut current_database,
                dep_databases: &dependent_cpp_crates,
                config,
            };
            (item.function)(data)?;

            if !item.is_const {
                current_database_saved = false;
            }
        } else {
            println!(
                "Unknown operation: {}. Supported operations: {}",
                operation,
                all_processing_steps
                    .iter()
                    .map(|item| &item.name)
                    .join(", ")
            );
        }
    }

    /*
    if exec_config.write_dependencies_local_paths {
    log::status(
    "Output Cargo.toml file will contain local paths of used dependencies \
      (use --no-local-paths to disable).",
    );
    } else {
    log::status(
    "Local paths will not be written to the output crate. Make sure all dependencies \
      are published before trying to compile the crate.",
    );
    }

    */

    /*
    parser_cpp_data.detect_signals_and_slots(
      &dependent_cpp_crates,
    )?;
    // TODO: rename `cpp_data_filters` to `parser_cpp_data_filters`
    if config.has_cpp_data_filters() {
      log::status("Running custom filters for C++ parser data");
      for filter in config.cpp_data_filters() {
        filter(&mut parser_cpp_data).chain_err(
          || "cpp_data_filter failed",
        )?;
      }
    }

    log::status("Post-processing parse result");
    let r = cpp_post_process(
      parser_cpp_data,
      dependent_cpp_crates,
      config.type_allocation_places(),
    )?;

    //...

    */

    for database in dependent_cpp_crates {
        workspace.put_crate(database, true);
    }

    if !current_database_saved {
        log::status("Saving data");
    }

    workspace.put_crate(current_database, current_database_saved);
    workspace.save_data()?;
    Ok(())
}
<|endoftext|>


<commit_before>

pub struct Usb {
}

pub struct Serial {
}

pub struct Network {
}

pub struct File {
}
<commit_msg>Add Network/File device<commit_after>
use std::io;
use std::fs;
use std::net;

pub struct Usb {}
pub struct Serial {}

#[derive(Debug)]
pub struct Network {
    _host: String,
    _port: u16,
    stream: net::TcpStream
}

impl Network {
    pub fn new(host: &str, port: u16) -> Network {
        let stream = net::TcpStream::connect((host, port)).unwrap();
        Network{
            _host: host.to_string(),
            _port: port,
            stream: stream
        }
    }
}

impl io::Write for Network {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stream.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stream.flush()
    }
}

#[derive(Debug)]
pub struct File {
    _path: String,
    fobj: fs::File
}

impl File {
    pub fn new(path: &str) -> File {
        let fobj = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(path)
            .unwrap();
        File{_path: path.to_string(), fobj: fobj}
    }
}

impl io::Write for File {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.fobj.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.fobj.flush()
    }
}
<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use error::NSError;
use objc::runtime::YES;
use objc_bringup::NSArray;
use sys::{MTLCopyAllDevices, MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::convert::{AsRef, From};
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use std::path::Path;
use {CommandQueue, CommandQueueError, CompileOptions, FromRaw, FromRawError, Library,
     LibraryError, LibraryErrorType, Size};

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 enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        unsafe {
            for i in 0..all_devices.count() {
                let device = all_devices.objectAtIndex(i);
                if device != nil {
                    devices_vec.push(Ok(Device(device)));
                } else {
                    devices_vec.push(Err(DeviceError::ConstructionFailed));
                }
            }
        }

        devices_vec
    }

    pub fn new_command_queue(&mut self) -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe { self.0.newCommandQueue() };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    pub fn new_command_queue_with_max_buffer_count(&mut self, max_command_buffer_count: usize)
                                                   -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe {
            self.0.newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    // TODO(George): This will panic in the metal lib if not called from a binary in a bundle. Need
    // to add proper error checking
    pub fn new_default_library(&mut self) -> Result<Library, LibraryError> {
        unsafe {
            let library = self.0.newDefaultLibrary();
            Ok(try!(FromRaw::from_raw(library)))
        }
    }

    #[allow(unused_variables)]
    pub fn new_library_with_file<P: AsRef<Path>>(&mut self, file_path: P)
                                                 -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn new_library_with_source<S: AsRef<str>>(&mut self, source: S,
                                                  compile_options: &CompileOptions)
                                                  -> Result<Library, LibraryError> {
        unsafe {
            let source = NSString::alloc(nil).init_str(source.as_ref());
            let options = compile_options.mtl_compile_options();
            let mut error = nil;
            let library = self.0.newLibraryWithSource_options_error(source, options, &mut error);
            if library == nil {
                Err(LibraryError {
                    ns_error: NSError::new(error),
                    error_type: LibraryErrorType::SourceError
                })
            } else {
                Ok(try!(FromRaw::from_raw(library)))
            }
        }
    }

    #[allow(unused_variables)]
    // TODO(George): `data` parameter should be of the correct type
    pub fn new_library_with_data(&mut self, data: ()) -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

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

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

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

    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) == YES }
    }
}

impl_from_into_raw!(Device, of protocol "MTLDevice");

/// 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
}

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

impl From<FromRawError> for DeviceError {
    fn from(error: FromRawError) -> Self {
        DeviceError::RawError(error)
    }
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::RawError(_) => "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"
            }
            DeviceError::RawError(_) => "There was an error with the device pointer",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &DeviceError::ConstructionFailed => None,
            &DeviceError::RawError(ref raw_err) => Some(raw_err),
        }
    }
}
<commit_msg>Remove unused function `_device_get_raw`<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use error::NSError;
use objc::runtime::YES;
use objc_bringup::NSArray;
use sys::{MTLCopyAllDevices, MTLCreateSystemDefaultDevice, MTLDevice};
use std::borrow::Cow;
use std::convert::{AsRef, From};
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use std::path::Path;
use {CommandQueue, CommandQueueError, CompileOptions, FromRaw, FromRawError, Library,
     LibraryError, LibraryErrorType, Size};

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 enumerate_all_system_devices() -> Vec<Result<Self, DeviceError>> {
        let all_devices = unsafe { MTLCopyAllDevices() };
        let mut devices_vec = vec![];

        unsafe {
            for i in 0..all_devices.count() {
                let device = all_devices.objectAtIndex(i);
                if device != nil {
                    devices_vec.push(Ok(Device(device)));
                } else {
                    devices_vec.push(Err(DeviceError::ConstructionFailed));
                }
            }
        }

        devices_vec
    }

    pub fn new_command_queue(&mut self) -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe { self.0.newCommandQueue() };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    pub fn new_command_queue_with_max_buffer_count(&mut self, max_command_buffer_count: usize)
                                                   -> Result<CommandQueue, CommandQueueError> {
        let command_queue = unsafe {
            self.0.newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        Ok(try!(FromRaw::from_raw(command_queue)))
    }

    // TODO(George): This will panic in the metal lib if not called from a binary in a bundle. Need
    // to add proper error checking
    pub fn new_default_library(&mut self) -> Result<Library, LibraryError> {
        unsafe {
            let library = self.0.newDefaultLibrary();
            Ok(try!(FromRaw::from_raw(library)))
        }
    }

    #[allow(unused_variables)]
    pub fn new_library_with_file<P: AsRef<Path>>(&mut self, file_path: P)
                                                 -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn new_library_with_source<S: AsRef<str>>(&mut self, source: S,
                                                  compile_options: &CompileOptions)
                                                  -> Result<Library, LibraryError> {
        unsafe {
            let source = NSString::alloc(nil).init_str(source.as_ref());
            let options = compile_options.mtl_compile_options();
            let mut error = nil;
            let library = self.0.newLibraryWithSource_options_error(source, options, &mut error);
            if library == nil {
                Err(LibraryError {
                    ns_error: NSError::new(error),
                    error_type: LibraryErrorType::SourceError
                })
            } else {
                Ok(try!(FromRaw::from_raw(library)))
            }
        }
    }

    #[allow(unused_variables)]
    // TODO(George): `data` parameter should be of the correct type
    pub fn new_library_with_data(&mut self, data: ()) -> Result<Library, LibraryError> {
        unimplemented!();
    }

    pub fn is_depth24_stencil8_pixel_format_supported(&self) -> bool {
        unsafe { self.0.depth24Stencil8PixelFormatSupported() == YES }
    }

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

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

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

    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) == YES }
    }
}

impl_from_into_raw!(Device, of protocol "MTLDevice");

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

impl From<FromRawError> for DeviceError {
    fn from(error: FromRawError) -> Self {
        DeviceError::RawError(error)
    }
}

impl Display for DeviceError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            DeviceError::ConstructionFailed => "DeviceError::ConstructionFailed",
            DeviceError::RawError(_) => "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"
            }
            DeviceError::RawError(_) => "There was an error with the device pointer",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match self {
            &DeviceError::ConstructionFailed => None,
            &DeviceError::RawError(ref raw_err) => Some(raw_err),
        }
    }
}
<|endoftext|>


<commit_before>//! This module contains the common driver code. Specific implementations for
//! different drivers exist elsewhere.

use error::Error;
use value::*;
use query::Query;

/// Gets the driver from a URL string using the protocol. For example a URL
/// of `postgres://localhost:5432/test_db` would look for a
/// `ardite-driver-postgres` crate, download the crate if it did not already
/// exist in the file system, and then return an instance initialized with the
/// `connect` static trait function.
pub fn get_driver<D: Driver>(url: &str) -> D {
  // TODO: implement
  unimplemented!();
}

pub trait Driver {
  /// Connects to a driver and returns a driver instance. After calling this
  /// the driver is ready to roll!
  fn connect(url: &str) -> Self;

  /// Set a value at a certain point in the driver.
  fn set(&self, pointer: Pointer, value: Value) -> Result<(), Error>;

  /// Get a value from a certain point in the driver. An optional query may be
  /// performed for more complex data selection.
  fn get(&self, pointer: Pointer, query: Option<Query>) -> Result<Value, Error>;

  /// Gets the schema for the driver. By default no schema is returned.
  fn get_schema(&self) -> Result<Schema, Error> {
    Ok(Schema::None)
  }
}
<commit_msg>refactor: connect may return error<commit_after>//! This module contains the common driver code. Specific implementations for
//! different drivers exist elsewhere.

use error::Error;
use value::*;
use query::Query;

/// Gets the driver from a URL string using the protocol. For example a URL
/// of `postgres://localhost:5432/test_db` would look for a
/// `ardite-driver-postgres` crate, download the crate if it did not already
/// exist in the file system, and then return an instance initialized with the
/// `connect` static trait function.
pub fn get_driver<D: Driver>(url: &str) -> D {
  // TODO: implement
  unimplemented!();
}

pub trait Driver {
  /// Connects to a driver and returns a driver instance. After calling this
  /// the driver is ready to roll!
  fn connect(url: &str) -> Result<&Self, Error>;

  /// Set a value at a certain point in the driver.
  fn set(&self, pointer: Pointer, value: Value) -> Result<(), Error>;

  /// Get a value from a certain point in the driver. An optional query may be
  /// performed for more complex data selection.
  fn get(&self, pointer: Pointer, query: Option<Query>) -> Result<Value, Error>;

  /// Gets the schema for the driver. By default no schema is returned.
  fn get_schema(&self) -> Result<Schema, Error> {
    Ok(Schema::None)
  }
}
<|endoftext|>


<commit_before>use std::fmt::{Display, Formatter, Result, Write};

use crate::datetime::*;
use crate::document::Document;
use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
use crate::key::Key;
use crate::repr::{Formatted, Repr, ValueRepr};
use crate::table::{DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_TABLE_DECOR};
use crate::value::{DEFAULT_LEADING_VALUE_DECOR, DEFAULT_VALUE_DECOR};
use crate::{Array, InlineTable, Item, Table, Value};

pub(crate) trait Encode {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result;
}

impl Encode for Key {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        let repr = self.to_repr();
        write!(
            buf,
            "{}{}{}",
            self.decor().prefix().unwrap_or(default_decor.0),
            repr,
            self.decor().suffix().unwrap_or(default_decor.1)
        )
    }
}

impl<'k> Encode for &'k [&'k Key] {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        for (i, key) in self.iter().enumerate() {
            let first = i == 0;
            let last = i + 1 == self.len();

            let prefix = if first {
                default_decor.0
            } else {
                DEFAULT_KEY_PATH_DECOR.0
            };
            let suffix = if last {
                default_decor.1
            } else {
                DEFAULT_KEY_PATH_DECOR.1
            };

            if !first {
                write!(buf, ".")?;
            }
            key.encode(buf, (prefix, suffix))?;
        }
        Ok(())
    }
}

impl<T> Encode for Formatted<T>
where
    T: ValueRepr,
{
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        let repr = self.to_repr();
        write!(
            buf,
            "{}{}{}",
            self.decor().prefix().unwrap_or(default_decor.0),
            repr,
            self.decor().suffix().unwrap_or(default_decor.1)
        )
    }
}

impl Encode for Array {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        write!(buf, "{}[", self.decor().prefix().unwrap_or(default_decor.0))?;

        for (i, elem) in self.iter().enumerate() {
            let inner_decor;
            if i == 0 {
                inner_decor = DEFAULT_LEADING_VALUE_DECOR;
            } else {
                inner_decor = DEFAULT_VALUE_DECOR;
                write!(buf, ",")?;
            }
            elem.encode(buf, inner_decor)?;
        }
        if self.trailing_comma() && !self.is_empty() {
            write!(buf, ",")?;
        }

        write!(buf, "{}", self.trailing())?;
        write!(buf, "]{}", self.decor().suffix().unwrap_or(default_decor.1))
    }
}

impl Encode for InlineTable {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        write!(
            buf,
            "{}{{",
            self.decor().prefix().unwrap_or(default_decor.0)
        )?;
        write!(buf, "{}", self.preamble)?;

        let children = self.get_values();
        for (i, (key_path, value)) in children.into_iter().enumerate() {
            if i != 0 {
                write!(buf, ",")?;
            }
            key_path.as_slice().encode(buf, DEFAULT_INLINE_KEY_DECOR)?;
            write!(buf, "=")?;
            value.encode(buf, DEFAULT_VALUE_DECOR)?;
        }

        write!(
            buf,
            "}}{}",
            self.decor().suffix().unwrap_or(default_decor.1)
        )
    }
}

impl Encode for Value {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        match self {
            Value::String(repr) => repr.encode(buf, default_decor),
            Value::Integer(repr) => repr.encode(buf, default_decor),
            Value::Float(repr) => repr.encode(buf, default_decor),
            Value::Boolean(repr) => repr.encode(buf, default_decor),
            Value::Datetime(repr) => repr.encode(buf, default_decor),
            Value::Array(array) => array.encode(buf, default_decor),
            Value::InlineTable(table) => table.encode(buf, default_decor),
        }
    }
}

impl Display for Document {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        let mut path = Vec::new();
        let mut last_position = 0;
        let mut tables = Vec::new();
        visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
            if let Some(pos) = t.position {
                last_position = pos;
            }
            tables.push((last_position, t, p.clone(), is_array));
            Ok(())
        })
        .unwrap();

        tables.sort_by_key(|&(id, _, _, _)| id);
        for (_, table, path, is_array) in tables {
            visit_table(f, table, &path, is_array)?;
        }
        self.trailing.fmt(f)
    }
}

fn visit_nested_tables<'t, F>(
    table: &'t Table,
    path: &mut Vec<&'t Key>,
    is_array_of_tables: bool,
    callback: &mut F,
) -> Result
where
    F: FnMut(&'t Table, &Vec<&'t Key>, bool) -> Result,
{
    callback(table, path, is_array_of_tables)?;

    for kv in table.items.values() {
        match kv.value {
            Item::Table(ref t) if !t.is_dotted() => {
                path.push(&kv.key);
                visit_nested_tables(t, path, false, callback)?;
                path.pop();
            }
            Item::ArrayOfTables(ref a) => {
                for t in a.iter() {
                    path.push(&kv.key);
                    visit_nested_tables(t, path, true, callback)?;
                    path.pop();
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn visit_table(
    buf: &mut dyn Write,
    table: &Table,
    path: &[&Key],
    is_array_of_tables: bool,
) -> Result {
    let children = table.get_values();

    if path.is_empty() {
        // don't print header for the root node
    } else if is_array_of_tables {
        write!(
            buf,
            "{}[[",
            table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0)
        )?;
        path.encode(buf, DEFAULT_KEY_PATH_DECOR)?;
        writeln!(
            buf,
            "]]{}",
            table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1)
        )?;
    } else if !(table.implicit && children.is_empty()) {
        write!(
            buf,
            "{}[",
            table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0)
        )?;
        path.encode(buf, DEFAULT_KEY_PATH_DECOR)?;
        writeln!(
            buf,
            "]{}",
            table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1)
        )?;
    }
    // print table body
    for (key_path, value) in children {
        key_path.as_slice().encode(buf, DEFAULT_KEY_DECOR)?;
        write!(buf, "=")?;
        value.encode(buf, DEFAULT_VALUE_DECOR)?;
        writeln!(buf)?;
    }
    Ok(())
}

impl ValueRepr for String {
    fn to_repr(&self) -> Repr {
        to_string_repr(self, None, None)
    }
}

pub(crate) fn to_string_repr(
    value: &str,
    style: Option<StringStyle>,
    literal: Option<bool>,
) -> Repr {
    let (style, literal) = match (style, literal) {
        (Some(style), Some(literal)) => (style, literal),
        (_, Some(literal)) => (infer_style(value).0, literal),
        (Some(style), _) => (style, infer_style(value).1),
        (_, _) => infer_style(value),
    };

    let mut output = String::with_capacity(value.len() * 2);
    if literal {
        output.push_str(style.literal_start());
        output.push_str(value);
        output.push_str(style.literal_end());
    } else {
        output.push_str(style.standard_start());
        for ch in value.chars() {
            match ch {
                '\u{8}' => output.push_str("\\b"),
                '\u{9}' => output.push_str("\\t"),
                '\u{a}' => match style {
                    StringStyle::NewlineTripple => output.push('\n'),
                    StringStyle::OnelineSingle => output.push_str("\\n"),
                    _ => unreachable!(),
                },
                '\u{c}' => output.push_str("\\f"),
                '\u{d}' => output.push_str("\\r"),
                '\u{22}' => output.push_str("\\\""),
                '\u{5c}' => output.push_str("\\\\"),
                c if c <= '\u{1f}' || c == '\u{7f}' => {
                    write!(output, "\\u{:04X}", ch as u32).unwrap();
                }
                ch => output.push(ch),
            }
        }
        output.push_str(style.standard_end());
    }

    Repr::new_unchecked(output)
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum StringStyle {
    NewlineTripple,
    OnelineTripple,
    OnelineSingle,
}

impl StringStyle {
    fn literal_start(self) -> &'static str {
        match self {
            Self::NewlineTripple => "'''\n",
            Self::OnelineTripple => "'''",
            Self::OnelineSingle => "'",
        }
    }
    fn literal_end(self) -> &'static str {
        match self {
            Self::NewlineTripple => "'''",
            Self::OnelineTripple => "'''",
            Self::OnelineSingle => "'",
        }
    }

    fn standard_start(self) -> &'static str {
        match self {
            Self::NewlineTripple => "\"\"\"\n",
            // note: OnelineTripple can happen if do_pretty wants to do
            // '''it's one line'''
            // but literal == false
            Self::OnelineTripple | Self::OnelineSingle => "\"",
        }
    }

    fn standard_end(self) -> &'static str {
        match self {
            Self::NewlineTripple => "\"\"\"",
            // note: OnelineTripple can happen if do_pretty wants to do
            // '''it's one line'''
            // but literal == false
            Self::OnelineTripple | Self::OnelineSingle => "\"",
        }
    }
}

fn infer_style(value: &str) -> (StringStyle, bool) {
    // For doing pretty prints we store in a new String
    // because there are too many cases where pretty cannot
    // work. We need to determine:
    // - if we are a "multi-line" pretty (if there are \n)
    // - if ['''] appears if multi or ['] if single
    // - if there are any invalid control characters
    //
    // Doing it any other way would require multiple passes
    // to determine if a pretty string works or not.
    let mut out = String::with_capacity(value.len() * 2);
    let mut ty = StringStyle::OnelineSingle;
    // found consecutive single quotes
    let mut max_found_singles = 0;
    let mut found_singles = 0;
    let mut prefer_literal = false;
    let mut can_be_pretty = true;

    for ch in value.chars() {
        if can_be_pretty {
            if ch == '\'' {
                found_singles += 1;
                if found_singles >= 3 {
                    can_be_pretty = false;
                }
            } else {
                if found_singles > max_found_singles {
                    max_found_singles = found_singles;
                }
                found_singles = 0
            }
            match ch {
                '\t' => {}
                '\\' => {
                    prefer_literal = true;
                }
                '\n' => ty = StringStyle::NewlineTripple,
                // Escape codes are needed if any ascii control
                // characters are present, including \b \f \r.
                c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false,
                _ => {}
            }
            out.push(ch);
        } else {
            // the string cannot be represented as pretty,
            // still check if it should be multiline
            if ch == '\n' {
                ty = StringStyle::NewlineTripple;
            }
        }
    }
    if found_singles > 0 && value.ends_with('\'') {
        // We cannot escape the ending quote so we must use """
        can_be_pretty = false;
    }
    if !prefer_literal {
        can_be_pretty = false;
    }
    if !can_be_pretty {
        debug_assert!(ty != StringStyle::OnelineTripple);
        return (ty, false);
    }
    if found_singles > max_found_singles {
        max_found_singles = found_singles;
    }
    debug_assert!(max_found_singles < 3);
    if ty == StringStyle::OnelineSingle && max_found_singles >= 1 {
        // no newlines, but must use ''' because it has ' in it
        ty = StringStyle::OnelineTripple;
    }
    (ty, true)
}

impl ValueRepr for i64 {
    fn to_repr(&self) -> Repr {
        Repr::new_unchecked(self.to_string())
    }
}

impl ValueRepr for f64 {
    fn to_repr(&self) -> Repr {
        to_f64_repr(*self)
    }
}

fn to_f64_repr(f: f64) -> Repr {
    let repr = match (f.is_sign_negative(), f.is_nan(), f == 0.0) {
        (true, true, _) => "-nan".to_owned(),
        (false, true, _) => "nan".to_owned(),
        (true, false, true) => "-0.0".to_owned(),
        (false, false, true) => "0.0".to_owned(),
        (_, false, false) => {
            if f % 1.0 == 0.0 {
                format!("{}.0", f)
            } else {
                format!("{}", f)
            }
        }
    };
    Repr::new_unchecked(repr)
}

impl ValueRepr for bool {
    fn to_repr(&self) -> Repr {
        Repr::new_unchecked(self.to_string())
    }
}

impl ValueRepr for Datetime {
    fn to_repr(&self) -> Repr {
        Repr::new_unchecked(self.to_string())
    }
}
<commit_msg>fix: Trailing space for default tables (#263)<commit_after>use std::fmt::{Display, Formatter, Result, Write};

use crate::datetime::*;
use crate::document::Document;
use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
use crate::key::Key;
use crate::repr::{Formatted, Repr, ValueRepr};
use crate::table::{DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_TABLE_DECOR};
use crate::value::{
    DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
};
use crate::{Array, InlineTable, Item, Table, Value};

pub(crate) trait Encode {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result;
}

impl Encode for Key {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        let repr = self.to_repr();
        write!(
            buf,
            "{}{}{}",
            self.decor().prefix().unwrap_or(default_decor.0),
            repr,
            self.decor().suffix().unwrap_or(default_decor.1)
        )
    }
}

impl<'k> Encode for &'k [&'k Key] {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        for (i, key) in self.iter().enumerate() {
            let first = i == 0;
            let last = i + 1 == self.len();

            let prefix = if first {
                default_decor.0
            } else {
                DEFAULT_KEY_PATH_DECOR.0
            };
            let suffix = if last {
                default_decor.1
            } else {
                DEFAULT_KEY_PATH_DECOR.1
            };

            if !first {
                write!(buf, ".")?;
            }
            key.encode(buf, (prefix, suffix))?;
        }
        Ok(())
    }
}

impl<T> Encode for Formatted<T>
where
    T: ValueRepr,
{
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        let repr = self.to_repr();
        write!(
            buf,
            "{}{}{}",
            self.decor().prefix().unwrap_or(default_decor.0),
            repr,
            self.decor().suffix().unwrap_or(default_decor.1)
        )
    }
}

impl Encode for Array {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        write!(buf, "{}[", self.decor().prefix().unwrap_or(default_decor.0))?;

        for (i, elem) in self.iter().enumerate() {
            let inner_decor;
            if i == 0 {
                inner_decor = DEFAULT_LEADING_VALUE_DECOR;
            } else {
                inner_decor = DEFAULT_VALUE_DECOR;
                write!(buf, ",")?;
            }
            elem.encode(buf, inner_decor)?;
        }
        if self.trailing_comma() && !self.is_empty() {
            write!(buf, ",")?;
        }

        write!(buf, "{}", self.trailing())?;
        write!(buf, "]{}", self.decor().suffix().unwrap_or(default_decor.1))
    }
}

impl Encode for InlineTable {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        write!(
            buf,
            "{}{{",
            self.decor().prefix().unwrap_or(default_decor.0)
        )?;
        write!(buf, "{}", self.preamble)?;

        let children = self.get_values();
        let len = children.len();
        for (i, (key_path, value)) in children.into_iter().enumerate() {
            if i != 0 {
                write!(buf, ",")?;
            }
            let inner_decor;
            if i == len - 1 {
                inner_decor = DEFAULT_TRAILING_VALUE_DECOR;
            } else {
                inner_decor = DEFAULT_VALUE_DECOR;
            }
            key_path.as_slice().encode(buf, DEFAULT_INLINE_KEY_DECOR)?;
            write!(buf, "=")?;
            value.encode(buf, inner_decor)?;
        }

        write!(
            buf,
            "}}{}",
            self.decor().suffix().unwrap_or(default_decor.1)
        )
    }
}

impl Encode for Value {
    fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result {
        match self {
            Value::String(repr) => repr.encode(buf, default_decor),
            Value::Integer(repr) => repr.encode(buf, default_decor),
            Value::Float(repr) => repr.encode(buf, default_decor),
            Value::Boolean(repr) => repr.encode(buf, default_decor),
            Value::Datetime(repr) => repr.encode(buf, default_decor),
            Value::Array(array) => array.encode(buf, default_decor),
            Value::InlineTable(table) => table.encode(buf, default_decor),
        }
    }
}

impl Display for Document {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        let mut path = Vec::new();
        let mut last_position = 0;
        let mut tables = Vec::new();
        visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
            if let Some(pos) = t.position {
                last_position = pos;
            }
            tables.push((last_position, t, p.clone(), is_array));
            Ok(())
        })
        .unwrap();

        tables.sort_by_key(|&(id, _, _, _)| id);
        for (_, table, path, is_array) in tables {
            visit_table(f, table, &path, is_array)?;
        }
        self.trailing.fmt(f)
    }
}

fn visit_nested_tables<'t, F>(
    table: &'t Table,
    path: &mut Vec<&'t Key>,
    is_array_of_tables: bool,
    callback: &mut F,
) -> Result
where
    F: FnMut(&'t Table, &Vec<&'t Key>, bool) -> Result,
{
    callback(table, path, is_array_of_tables)?;

    for kv in table.items.values() {
        match kv.value {
            Item::Table(ref t) if !t.is_dotted() => {
                path.push(&kv.key);
                visit_nested_tables(t, path, false, callback)?;
                path.pop();
            }
            Item::ArrayOfTables(ref a) => {
                for t in a.iter() {
                    path.push(&kv.key);
                    visit_nested_tables(t, path, true, callback)?;
                    path.pop();
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn visit_table(
    buf: &mut dyn Write,
    table: &Table,
    path: &[&Key],
    is_array_of_tables: bool,
) -> Result {
    let children = table.get_values();

    if path.is_empty() {
        // don't print header for the root node
    } else if is_array_of_tables {
        write!(
            buf,
            "{}[[",
            table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0)
        )?;
        path.encode(buf, DEFAULT_KEY_PATH_DECOR)?;
        writeln!(
            buf,
            "]]{}",
            table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1)
        )?;
    } else if !(table.implicit && children.is_empty()) {
        write!(
            buf,
            "{}[",
            table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0)
        )?;
        path.encode(buf, DEFAULT_KEY_PATH_DECOR)?;
        writeln!(
            buf,
            "]{}",
            table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1)
        )?;
    }
    // print table body
    for (key_path, value) in children {
        key_path.as_slice().encode(buf, DEFAULT_KEY_DECOR)?;
        write!(buf, "=")?;
        value.encode(buf, DEFAULT_VALUE_DECOR)?;
        writeln!(buf)?;
    }
    Ok(())
}

impl ValueRepr for String {
    fn to_repr(&self) -> Repr {
        to_string_repr(self, None, None)
    }
}

pub(crate) fn to_string_repr(
    value: &str,
    style: Option<StringStyle>,
    literal: Option<bool>,
) -> Repr {
    let (style, literal) = match (style, literal) {
        (Some(style), Some(literal)) => (style, literal),
        (_, Some(literal)) => (infer_style(value).0, literal),
        (Some(style), _) => (style, infer_style(value).1),
        (_, _) => infer_style(value),
    };

    let mut output = String::with_capacity(value.len() * 2);
    if literal {
        output.push_str(style.literal_start());
        output.push_str(value);
        output.push_str(style.literal_end());
    } else {
        output.push_str(style.standard_start());
        for ch in value.chars() {
            match ch {
                '\u{8}' => output.push_str("\\b"),
                '\u{9}' => output.push_str("\\t"),
                '\u{a}' => match style {
                    StringStyle::NewlineTripple => output.push('\n'),
                    StringStyle::OnelineSingle => output.push_str("\\n"),
                    _ => unreachable!(),
                },
                '\u{c}' => output.push_str("\\f"),
                '\u{d}' => output.push_str("\\r"),
                '\u{22}' => output.push_str("\\\""),
                '\u{5c}' => output.push_str("\\\\"),
                c if c <= '\u{1f}' || c == '\u{7f}' => {
                    write!(output, "\\u{:04X}", ch as u32).unwrap();
                }
                ch => output.push(ch),
            }
        }
        output.push_str(style.standard_end());
    }

    Repr::new_unchecked(output)
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum StringStyle {
    NewlineTripple,
    OnelineTripple,
    OnelineSingle,
}

impl StringStyle {
    fn literal_start(self) -> &'static str {
        match self {
            Self::NewlineTripple => "'''\n",
            Self::OnelineTripple => "'''",
            Self::OnelineSingle => "'",
        }
    }
    fn literal_end(self) -> &'static str {
        match self {
            Self::NewlineTripple => "'''",
            Self::OnelineTripple => "'''",
            Self::OnelineSingle => "'",
        }
    }

    fn standard_start(self) -> &'static str {
        match self {
            Self::NewlineTripple => "\"\"\"\n",
            // note: OnelineTripple can happen if do_pretty wants to do
            // '''it's one line'''
            // but literal == false
            Self::OnelineTripple | Self::OnelineSingle => "\"",
        }
    }

    fn standard_end(self) -> &'static str {
        match self {
            Self::NewlineTripple => "\"\"\"",
            // note: OnelineTripple can happen if do_pretty wants to do
            // '''it's one line'''
            // but literal == false
            Self::OnelineTripple | Self::OnelineSingle => "\"",
        }
    }
}

fn infer_style(value: &str) -> (StringStyle, bool) {
    // For doing pretty prints we store in a new String
    // because there are too many cases where pretty cannot
    // work. We need to determine:
    // - if we are a "multi-line" pretty (if there are \n)
    // - if ['''] appears if multi or ['] if single
    // - if there are any invalid control characters
    //
    // Doing it any other way would require multiple passes
    // to determine if a pretty string works or not.
    let mut out = String::with_capacity(value.len() * 2);
    let mut ty = StringStyle::OnelineSingle;
    // found consecutive single quotes
    let mut max_found_singles = 0;
    let mut found_singles = 0;
    let mut prefer_literal = false;
    let mut can_be_pretty = true;

    for ch in value.chars() {
        if can_be_pretty {
            if ch == '\'' {
                found_singles += 1;
                if found_singles >= 3 {
                    can_be_pretty = false;
                }
            } else {
                if found_singles > max_found_singles {
                    max_found_singles = found_singles;
                }
                found_singles = 0
            }
            match ch {
                '\t' => {}
                '\\' => {
                    prefer_literal = true;
                }
                '\n' => ty = StringStyle::NewlineTripple,
                // Escape codes are needed if any ascii control
                // characters are present, including \b \f \r.
                c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false,
                _ => {}
            }
            out.push(ch);
        } else {
            // the string cannot be represented as pretty,
            // still check if it should be multiline
            if ch == '\n' {
                ty = StringStyle::NewlineTripple;
            }
        }
    }
    if found_singles > 0 && value.ends_with('\'') {
        // We cannot escape the ending quote so we must use """
        can_be_pretty = false;
    }
    if !prefer_literal {
        can_be_pretty = false;
    }
    if !can_be_pretty {
        debug_assert!(ty != StringStyle::OnelineTripple);
        return (ty, false);
    }
    if found_singles > max_found_singles {
        max_found_singles = found_singles;
    }
    debug_assert!(max_found_singles < 3);
    if ty == StringStyle::OnelineSingle && max_found_singles >= 1 {
        // no newlines, but must use ''' because it has ' in it
        ty = StringStyle::OnelineTripple;
    }
    (ty, true)
}

impl ValueRepr for i64 {
    fn to_repr(&self) -> Repr {
        Repr::new_unchecked(self.to_string())
    }
}

impl ValueRepr for f64 {
    fn to_repr(&self) -> Repr {
        to_f64_repr(*self)
    }
}

fn to_f64_repr(f: f64) -> Repr {
    let repr = match (f.is_sign_negative(), f.is_nan(), f == 0.0) {
        (true, true, _) => "-nan".to_owned(),
        (false, true, _) => "nan".to_owned(),
        (true, false, true) => "-0.0".to_owned(),
        (false, false, true) => "0.0".to_owned(),
        (_, false, false) => {
            if f % 1.0 == 0.0 {
                format!("{}.0", f)
            } else {
                format!("{}", f)
            }
        }
    };
    Repr::new_unchecked(repr)
}

impl ValueRepr for bool {
    fn to_repr(&self) -> Repr {
        Repr::new_unchecked(self.to_string())
    }
}

impl ValueRepr for Datetime {
    fn to_repr(&self) -> Repr {
        Repr::new_unchecked(self.to_string())
    }
}
<|endoftext|>