<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::io::Write;
use std::io::stderr;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::ops::Deref;

use error::RuntimeErrorKind as EK;
use error::RuntimeError as RE;
use error::ResultExt;
use runtime::Runtime;

use clap::ArgMatches;
use log::{Log, LogLevel, LogRecord, LogMetadata};
use toml::Value;
use toml_query::read::TomlValueReadExt;
use handlebars::Handlebars;

type ModuleName = String;
type Result<T> = ::std::result::Result<T, RE>;

enum LogDestination {
    Stderr,
    File(Arc<Mutex<::std::fs::File>>),
}

impl Default for LogDestination {
    fn default() -> LogDestination {
        LogDestination::Stderr
    }
}

struct ModuleSettings {
    enabled:        bool,
    level:          Option<LogLevel>,

    #[allow(unused)]
    destinations:   Option<Vec<LogDestination>>,
}

/// Logger implementation for `log` crate.
pub struct ImagLogger {
    global_loglevel     : LogLevel,

    #[allow(unused)]
    global_destinations : Vec<LogDestination>,
    // global_format_trace : ,
    // global_format_debug : ,
    // global_format_info  : ,
    // global_format_warn  : ,
    // global_format_error : ,
    module_settings     : BTreeMap<ModuleName, ModuleSettings>,

    handlebars: Handlebars,
}

impl ImagLogger {

    /// Create a new ImagLogger object with a certain level
    pub fn new(matches: &ArgMatches, config: Option<&Value>) -> Result<ImagLogger> {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(::handlebars::no_escape);

        ::libimaginteraction::format::register_all_color_helpers(&mut handlebars);
        ::libimaginteraction::format::register_all_format_helpers(&mut handlebars);

        {
            let fmt = try!(aggregate_global_format_trace(config));
            try!(handlebars.register_template_string("TRACE", fmt)); // name must be uppercase
        }
        {
            let fmt = try!(aggregate_global_format_debug(config));
            try!(handlebars.register_template_string("DEBUG", fmt)); // name must be uppercase
        }
        {
            let fmt = try!(aggregate_global_format_info(config));
            try!(handlebars.register_template_string("INFO", fmt)); // name must be uppercase
        }
        {
            let fmt = try!(aggregate_global_format_warn(config));
            try!(handlebars.register_template_string("WARN", fmt)); // name must be uppercase
        }
        {
            let fmt = try!(aggregate_global_format_error(config));
            try!(handlebars.register_template_string("ERROR", fmt)); // name must be uppercase
        }

        Ok(ImagLogger {
            global_loglevel     : try!(aggregate_global_loglevel(matches, config)),
            global_destinations : try!(aggregate_global_destinations(matches, config)),
            module_settings     : try!(aggregate_module_settings(matches, config)),
            handlebars          : handlebars,
        })
    }

    pub fn global_loglevel(&self) -> LogLevel {
        self.global_loglevel
    }

}

impl Log for ImagLogger {

    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.global_loglevel
    }

    fn log(&self, record: &LogRecord) {
        if record.location().module_path().starts_with("handlebars") {
            // This is a ugly, yet necessary hack. When logging, we use handlebars for templating.
            // But as the handlebars library itselfs logs via a normal logging macro ("debug!()"),
            // we have a recursion in our chain.
            //
            // To prevent this recursion, we return here.
            //
            // (As of handlebars 0.29.0 - please check whether you can update handlebars if you see
            // this. Hopefully the next version has a compiletime flag to disable logging)
            return;
        }

        let mut data = BTreeMap::new();

        {
            data.insert("level",        format!("{}", record.level()));
            data.insert("module_path",  String::from(record.location().module_path()));
            data.insert("file",         String::from(record.location().file()));
            data.insert("line",         format!("{}", record.location().line()));
            data.insert("target",       String::from(record.target()));
            data.insert("message",      format!("{}", record.args()));
        }

        let logtext = self
            .handlebars
            .render(&format!("{}", record.level()), &data)
            .unwrap_or_else(|e| format!("Failed rendering logging data: {:?}\n", e));

        let log_to_destination = |d: &LogDestination| match d {
            &LogDestination::Stderr => {
                let _ = write!(stderr(), "{}\n", logtext);
            },
            &LogDestination::File(ref arc_mutex_logdest) => {
                // if there is an error in the lock, we cannot do anything. So we ignore it here.
                let _ = arc_mutex_logdest
                    .deref()
                    .lock()
                    .map(|mut logdest| {
                        write!(logdest, "{}\n", logtext)
                    });
            }
        };

        // hack to get the right target configuration.
        // If there is no element here, we use the empty string which automatically drops through
        // to the unwrap_or_else() case
        let record_target = record
            .target()
            .split("::")
            .next()
            .unwrap_or("");

        self.module_settings
            .get(record_target)
            .map(|module_setting| {
                let set = module_setting.enabled &&
                    module_setting.level.unwrap_or(self.global_loglevel) >= record.level();

                if set {
                    module_setting.destinations.as_ref().map(|destinations| for d in destinations {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    });

                    for d in self.global_destinations.iter() {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    }
                }
            })
        .unwrap_or_else(|| {
            if self.global_loglevel >= record.level() {
                // Yes, we log
                for d in self.global_destinations.iter() {
                    // If there's an error, we cannot do anything, can we?
                    let _ = log_to_destination(&d);
                }
            }
        });
    }
}

fn match_log_level_str(s: &str) -> Result<LogLevel> {
    match s {
        "trace" => Ok(LogLevel::Trace),
        "debug" => Ok(LogLevel::Debug),
        "info"  => Ok(LogLevel::Info),
        "warn"  => Ok(LogLevel::Warn),
        "error" => Ok(LogLevel::Error),
        _       => return Err(RE::from_kind(EK::InvalidLogLevelSpec)),
    }
}

fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Value>)
    -> Result<LogLevel>
{
    fn get_arg_loglevel(matches: &ArgMatches) -> Result<Option<LogLevel>> {
        if matches.is_present(Runtime::arg_debugging_name()) {
            return Ok(Some(LogLevel::Debug))
        }

        if matches.is_present(Runtime::arg_verbosity_name()) {
            return Ok(Some(LogLevel::Info))
        }

        match matches.value_of(Runtime::arg_verbosity_name()) {
            Some(v) => match_log_level_str(v).map(Some),
            None    => Ok(None),
        }
    }

    if let Some(cfg) = config {
        let cfg_loglevel = match cfg.read("imag.logging.level") {
            Ok(Some(&Value::String(ref s))) => match_log_level_str(s),
            Ok(Some(_)) => {
                let path = "imag.logging.level".to_owned();
                let ty   = "String";
                return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => return Err(RE::from_kind(EK::GlobalLogLevelConfigMissing)),
            Err(e)      => return Err(e).map_err(From::from),
        }?;

        if let Some(cli_loglevel) = get_arg_loglevel(matches)? {
            if cli_loglevel > cfg_loglevel {
                return Ok(cli_loglevel)
            }
        }

        Ok(cfg_loglevel)

    } else {
        get_arg_loglevel(matches).map(|o| o.unwrap_or(LogLevel::Info))
    }
}

fn translate_destination(raw: &str) -> Result<LogDestination> {
    use std::fs::OpenOptions;

    match raw {
        "-" => Ok(LogDestination::Stderr),
        other => {
            OpenOptions::new()
                .append(true)
                .create(true)
                .open(other)
                .map(Mutex::new)
                .map(Arc::new)
                .map(LogDestination::File)
                .chain_err(|| EK::IOLogFileOpenError)
        }
    }
}

fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
    raw.iter()
        .fold(Ok(vec![]), |acc, val| {
            acc.and_then(|mut v| {
                let dest = match *val {
                    Value::String(ref s) => try!(translate_destination(s)),
                    _ => {
                        let path = "imag.logging.modules.<mod>.destinations".to_owned();
                        let ty   = "Array<String>";
                        return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                    },
                };
                v.push(dest);
                Ok(v)
            })
        })
}

fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
    -> Result<Vec<LogDestination>>
{

    match config {
        Some(cfg) => match cfg.read("imag.logging.destinations") {
            Ok(Some(&Value::Array(ref a))) => translate_destinations(a),
            Ok(Some(_)) => {
                let path = "imag.logging.destinations".to_owned();
                let ty   = "Array";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => Err(RE::from_kind(EK::GlobalDestinationConfigMissing)),
            Err(e)      => Err(e).map_err(From::from),
        },
        None => {
            if let Some(values) = matches.value_of(Runtime::arg_logdest_name()) {
                // parse logdest specification from commandline

                values.split(",")
                    .fold(Ok(vec![]), move |acc, dest| {
                        acc.and_then(|mut v| {
                            v.push(try!(translate_destination(dest)));
                            Ok(v)
                        })
                    })
            } else {
                Ok(vec![ LogDestination::default() ])
            }
        }
    }
}

macro_rules! aggregate_global_format {
    ($read_str:expr, $error_kind_if_missing:expr, $config:expr) => {
        match try!($config.ok_or(RE::from_kind($error_kind_if_missing))).read($read_str) {
            Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
            Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError($read_str.to_owned(), "String"))),
            Ok(None)    => Err(RE::from_kind($error_kind_if_missing)),
            Err(e)      => Err(e).map_err(From::from),
        }
    };
}

fn aggregate_global_format_trace(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.trace",
                            EK::ConfigMissingLoggingFormatTrace,
                            config)
}

fn aggregate_global_format_debug(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.debug",
                            EK::ConfigMissingLoggingFormatDebug,
                            config)
}

fn aggregate_global_format_info(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.info",
                            EK::ConfigMissingLoggingFormatInfo,
                            config)
}

fn aggregate_global_format_warn(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.warn",
                            EK::ConfigMissingLoggingFormatWarn,
                            config)
}

fn aggregate_global_format_error(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.error",
                            EK::ConfigMissingLoggingFormatError,
                            config)
}

fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
    -> Result<BTreeMap<ModuleName, ModuleSettings>>
{
    match config {
        Some(cfg) => match cfg.read("imag.logging.modules") {
            Ok(Some(&Value::Table(ref t))) => {
                // translate the module settings from the table `t`
                let mut settings = BTreeMap::new();

                for (module_name, v) in t {
                    let destinations = try!(match v.read("destinations") {
                        Ok(Some(&Value::Array(ref a))) => translate_destinations(a).map(Some),
                        Ok(None)    => Ok(None),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.destinations".to_owned();
                            let ty = "Array";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    });

                    let level = try!(match v.read("level") {
                        Ok(Some(&Value::String(ref s))) => match_log_level_str(s).map(Some),
                        Ok(None)    => Ok(None),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.level".to_owned();
                            let ty = "String";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    });

                    let enabled = try!(match v.read("enabled") {
                        Ok(Some(&Value::Boolean(b))) => Ok(b),
                        Ok(None)    => Ok(false),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.enabled".to_owned();
                            let ty = "Boolean";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    });

                    let module_settings = ModuleSettings {
                        enabled: enabled,
                        level: level,
                        destinations: destinations,
                    };

                    // We don't care whether there was a value, we override it.
                    let _ = settings.insert(module_name.to_owned(), module_settings);
                }

                Ok(settings)
            },
            Ok(Some(_)) => {
                let path = "imag.logging.modules".to_owned();
                let ty = "Table";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => {
                // No modules configured. This is okay!
                Ok(BTreeMap::new())
            },
            Err(e) => Err(e).map_err(From::from),
        },
        None => {
            write!(stderr(), "No Configuration.").ok();
            write!(stderr(), "cannot find module-settings for logging.").ok();
            write!(stderr(), "Will use global defaults").ok();

            Ok(BTreeMap::new())
        }
    }
}

<commit_msg>Use ? operator instead of try!() macro<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::io::Write;
use std::io::stderr;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::ops::Deref;

use error::RuntimeErrorKind as EK;
use error::RuntimeError as RE;
use error::ResultExt;
use runtime::Runtime;

use clap::ArgMatches;
use log::{Log, LogLevel, LogRecord, LogMetadata};
use toml::Value;
use toml_query::read::TomlValueReadExt;
use handlebars::Handlebars;

type ModuleName = String;
type Result<T> = ::std::result::Result<T, RE>;

enum LogDestination {
    Stderr,
    File(Arc<Mutex<::std::fs::File>>),
}

impl Default for LogDestination {
    fn default() -> LogDestination {
        LogDestination::Stderr
    }
}

struct ModuleSettings {
    enabled:        bool,
    level:          Option<LogLevel>,

    #[allow(unused)]
    destinations:   Option<Vec<LogDestination>>,
}

/// Logger implementation for `log` crate.
pub struct ImagLogger {
    global_loglevel     : LogLevel,

    #[allow(unused)]
    global_destinations : Vec<LogDestination>,
    // global_format_trace : ,
    // global_format_debug : ,
    // global_format_info  : ,
    // global_format_warn  : ,
    // global_format_error : ,
    module_settings     : BTreeMap<ModuleName, ModuleSettings>,

    handlebars: Handlebars,
}

impl ImagLogger {

    /// Create a new ImagLogger object with a certain level
    pub fn new(matches: &ArgMatches, config: Option<&Value>) -> Result<ImagLogger> {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(::handlebars::no_escape);

        ::libimaginteraction::format::register_all_color_helpers(&mut handlebars);
        ::libimaginteraction::format::register_all_format_helpers(&mut handlebars);

        {
            let fmt = aggregate_global_format_trace(config)?;
            handlebars.register_template_string("TRACE", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_debug(config)?;
            handlebars.register_template_string("DEBUG", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_info(config)?;
            handlebars.register_template_string("INFO", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_warn(config)?;
            handlebars.register_template_string("WARN", fmt)?; // name must be uppercase
        }
        {
            let fmt = aggregate_global_format_error(config)?;
            handlebars.register_template_string("ERROR", fmt)?; // name must be uppercase
        }

        Ok(ImagLogger {
            global_loglevel     : aggregate_global_loglevel(matches, config)?,
            global_destinations : aggregate_global_destinations(matches, config)?,
            module_settings     : aggregate_module_settings(matches, config)?,
            handlebars          : handlebars,
        })
    }

    pub fn global_loglevel(&self) -> LogLevel {
        self.global_loglevel
    }

}

impl Log for ImagLogger {

    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.global_loglevel
    }

    fn log(&self, record: &LogRecord) {
        if record.location().module_path().starts_with("handlebars") {
            // This is a ugly, yet necessary hack. When logging, we use handlebars for templating.
            // But as the handlebars library itselfs logs via a normal logging macro ("debug!()"),
            // we have a recursion in our chain.
            //
            // To prevent this recursion, we return here.
            //
            // (As of handlebars 0.29.0 - please check whether you can update handlebars if you see
            // this. Hopefully the next version has a compiletime flag to disable logging)
            return;
        }

        let mut data = BTreeMap::new();

        {
            data.insert("level",        format!("{}", record.level()));
            data.insert("module_path",  String::from(record.location().module_path()));
            data.insert("file",         String::from(record.location().file()));
            data.insert("line",         format!("{}", record.location().line()));
            data.insert("target",       String::from(record.target()));
            data.insert("message",      format!("{}", record.args()));
        }

        let logtext = self
            .handlebars
            .render(&format!("{}", record.level()), &data)
            .unwrap_or_else(|e| format!("Failed rendering logging data: {:?}\n", e));

        let log_to_destination = |d: &LogDestination| match d {
            &LogDestination::Stderr => {
                let _ = write!(stderr(), "{}\n", logtext);
            },
            &LogDestination::File(ref arc_mutex_logdest) => {
                // if there is an error in the lock, we cannot do anything. So we ignore it here.
                let _ = arc_mutex_logdest
                    .deref()
                    .lock()
                    .map(|mut logdest| {
                        write!(logdest, "{}\n", logtext)
                    });
            }
        };

        // hack to get the right target configuration.
        // If there is no element here, we use the empty string which automatically drops through
        // to the unwrap_or_else() case
        let record_target = record
            .target()
            .split("::")
            .next()
            .unwrap_or("");

        self.module_settings
            .get(record_target)
            .map(|module_setting| {
                let set = module_setting.enabled &&
                    module_setting.level.unwrap_or(self.global_loglevel) >= record.level();

                if set {
                    module_setting.destinations.as_ref().map(|destinations| for d in destinations {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    });

                    for d in self.global_destinations.iter() {
                        // If there's an error, we cannot do anything, can we?
                        let _ = log_to_destination(&d);
                    }
                }
            })
        .unwrap_or_else(|| {
            if self.global_loglevel >= record.level() {
                // Yes, we log
                for d in self.global_destinations.iter() {
                    // If there's an error, we cannot do anything, can we?
                    let _ = log_to_destination(&d);
                }
            }
        });
    }
}

fn match_log_level_str(s: &str) -> Result<LogLevel> {
    match s {
        "trace" => Ok(LogLevel::Trace),
        "debug" => Ok(LogLevel::Debug),
        "info"  => Ok(LogLevel::Info),
        "warn"  => Ok(LogLevel::Warn),
        "error" => Ok(LogLevel::Error),
        _       => return Err(RE::from_kind(EK::InvalidLogLevelSpec)),
    }
}

fn aggregate_global_loglevel(matches: &ArgMatches, config: Option<&Value>)
    -> Result<LogLevel>
{
    fn get_arg_loglevel(matches: &ArgMatches) -> Result<Option<LogLevel>> {
        if matches.is_present(Runtime::arg_debugging_name()) {
            return Ok(Some(LogLevel::Debug))
        }

        if matches.is_present(Runtime::arg_verbosity_name()) {
            return Ok(Some(LogLevel::Info))
        }

        match matches.value_of(Runtime::arg_verbosity_name()) {
            Some(v) => match_log_level_str(v).map(Some),
            None    => Ok(None),
        }
    }

    if let Some(cfg) = config {
        let cfg_loglevel = match cfg.read("imag.logging.level") {
            Ok(Some(&Value::String(ref s))) => match_log_level_str(s),
            Ok(Some(_)) => {
                let path = "imag.logging.level".to_owned();
                let ty   = "String";
                return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => return Err(RE::from_kind(EK::GlobalLogLevelConfigMissing)),
            Err(e)      => return Err(e).map_err(From::from),
        }?;

        if let Some(cli_loglevel) = get_arg_loglevel(matches)? {
            if cli_loglevel > cfg_loglevel {
                return Ok(cli_loglevel)
            }
        }

        Ok(cfg_loglevel)

    } else {
        get_arg_loglevel(matches).map(|o| o.unwrap_or(LogLevel::Info))
    }
}

fn translate_destination(raw: &str) -> Result<LogDestination> {
    use std::fs::OpenOptions;

    match raw {
        "-" => Ok(LogDestination::Stderr),
        other => {
            OpenOptions::new()
                .append(true)
                .create(true)
                .open(other)
                .map(Mutex::new)
                .map(Arc::new)
                .map(LogDestination::File)
                .chain_err(|| EK::IOLogFileOpenError)
        }
    }
}

fn translate_destinations(raw: &Vec<Value>) -> Result<Vec<LogDestination>> {
    raw.iter()
        .fold(Ok(vec![]), |acc, val| {
            acc.and_then(|mut v| {
                let dest = match *val {
                    Value::String(ref s) => translate_destination(s)?,
                    _ => {
                        let path = "imag.logging.modules.<mod>.destinations".to_owned();
                        let ty   = "Array<String>";
                        return Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                    },
                };
                v.push(dest);
                Ok(v)
            })
        })
}

fn aggregate_global_destinations(matches: &ArgMatches, config: Option<&Value>)
    -> Result<Vec<LogDestination>>
{

    match config {
        Some(cfg) => match cfg.read("imag.logging.destinations") {
            Ok(Some(&Value::Array(ref a))) => translate_destinations(a),
            Ok(Some(_)) => {
                let path = "imag.logging.destinations".to_owned();
                let ty   = "Array";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => Err(RE::from_kind(EK::GlobalDestinationConfigMissing)),
            Err(e)      => Err(e).map_err(From::from),
        },
        None => {
            if let Some(values) = matches.value_of(Runtime::arg_logdest_name()) {
                // parse logdest specification from commandline

                values.split(",")
                    .fold(Ok(vec![]), move |acc, dest| {
                        acc.and_then(|mut v| {
                            v.push(translate_destination(dest)?);
                            Ok(v)
                        })
                    })
            } else {
                Ok(vec![ LogDestination::default() ])
            }
        }
    }
}

macro_rules! aggregate_global_format {
    ($read_str:expr, $error_kind_if_missing:expr, $config:expr) => {
        match try!($config.ok_or(RE::from_kind($error_kind_if_missing))).read($read_str) {
            Ok(Some(&Value::String(ref s))) => Ok(s.clone()),
            Ok(Some(_)) => Err(RE::from_kind(EK::ConfigTypeError($read_str.to_owned(), "String"))),
            Ok(None)    => Err(RE::from_kind($error_kind_if_missing)),
            Err(e)      => Err(e).map_err(From::from),
        }
    };
}

fn aggregate_global_format_trace(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.trace",
                            EK::ConfigMissingLoggingFormatTrace,
                            config)
}

fn aggregate_global_format_debug(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.debug",
                            EK::ConfigMissingLoggingFormatDebug,
                            config)
}

fn aggregate_global_format_info(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.info",
                            EK::ConfigMissingLoggingFormatInfo,
                            config)
}

fn aggregate_global_format_warn(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.warn",
                            EK::ConfigMissingLoggingFormatWarn,
                            config)
}

fn aggregate_global_format_error(config: Option<&Value>)
    -> Result<String>
{
    aggregate_global_format!("imag.logging.format.error",
                            EK::ConfigMissingLoggingFormatError,
                            config)
}

fn aggregate_module_settings(_matches: &ArgMatches, config: Option<&Value>)
    -> Result<BTreeMap<ModuleName, ModuleSettings>>
{
    match config {
        Some(cfg) => match cfg.read("imag.logging.modules") {
            Ok(Some(&Value::Table(ref t))) => {
                // translate the module settings from the table `t`
                let mut settings = BTreeMap::new();

                for (module_name, v) in t {
                    let destinations = match v.read("destinations") {
                        Ok(Some(&Value::Array(ref a))) => translate_destinations(a).map(Some),
                        Ok(None)    => Ok(None),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.destinations".to_owned();
                            let ty = "Array";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    }?;

                    let level = match v.read("level") {
                        Ok(Some(&Value::String(ref s))) => match_log_level_str(s).map(Some),
                        Ok(None)    => Ok(None),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.level".to_owned();
                            let ty = "String";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    }?;

                    let enabled = match v.read("enabled") {
                        Ok(Some(&Value::Boolean(b))) => Ok(b),
                        Ok(None)    => Ok(false),
                        Ok(Some(_)) => {
                            let path = "imag.logging.modules.<mod>.enabled".to_owned();
                            let ty = "Boolean";
                            Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
                        },
                        Err(e)      => Err(e).map_err(From::from),
                    }?;

                    let module_settings = ModuleSettings {
                        enabled: enabled,
                        level: level,
                        destinations: destinations,
                    };

                    // We don't care whether there was a value, we override it.
                    let _ = settings.insert(module_name.to_owned(), module_settings);
                }

                Ok(settings)
            },
            Ok(Some(_)) => {
                let path = "imag.logging.modules".to_owned();
                let ty = "Table";
                Err(RE::from_kind(EK::ConfigTypeError(path, ty)))
            },
            Ok(None)    => {
                // No modules configured. This is okay!
                Ok(BTreeMap::new())
            },
            Err(e) => Err(e).map_err(From::from),
        },
        None => {
            write!(stderr(), "No Configuration.").ok();
            write!(stderr(), "cannot find module-settings for logging.").ok();
            write!(stderr(), "Will use global defaults").ok();

            Ok(BTreeMap::new())
        }
    }
}

<|endoftext|>


<commit_before>

use at::{AtRust, AtTrigger};
use plugins::get;

use select::document::Document;
use select::predicate::{Attr, Class, Name};
use rand::Rng;
use rand;

pub fn eksi(trigger: &AtTrigger, at: &AtRust) {
    let url = format!("https://eksisozluk.com/{}", trigger.command_message);
    let body = get(&url[..]);

    let body_str = body.unwrap();
    let document = Document::from_str(&body_str[..]);

    let mut entries: Vec<String> = Vec::new();

    for entry in document.find(Attr("id", "entry-list")).find(Name("li")).iter() {
        let entry_text = entry.find(Class("content")).first().unwrap().text();
        // FIXME: Do I really need to clone text?
        entries.push(entry_text.clone());
    }

    let mut rng = rand::thread_rng();

    at.reply(trigger, &entries[rng.gen::<usize>() % entries.len()][..]);
}
<commit_msg>Remove rng variable<commit_after>

use at::{AtRust, AtTrigger};
use plugins::get;

use select::document::Document;
use select::predicate::{Attr, Class, Name};
use rand;

pub fn eksi(trigger: &AtTrigger, at: &AtRust) {
    let url = format!("https://eksisozluk.com/{}", trigger.command_message);
    let body = get(&url[..]);

    let body_str = body.unwrap();
    let document = Document::from_str(&body_str[..]);

    let mut entries: Vec<String> = Vec::new();

    for entry in document.find(Attr("id", "entry-list")).find(Name("li")).iter() {
        let entry_text = entry.find(Class("content")).first().unwrap().text();
        // FIXME: Do I really need to clone text?
        entries.push(entry_text.clone());
    }

    at.reply(trigger, &entries[rand::random::<usize>() % entries.len()][..]);
}
<|endoftext|>


<commit_before>extern crate time;

extern crate hyper;
extern crate iron;
extern crate iron_test;
extern crate staticfile;

#[cfg(feature = "cache")]
mod cache {
    use time;
    use time::{Timespec};

    #[cfg(feature = "cache")]
    use std::time::Duration;

    use iron::{Handler, Url};
    use iron::method::Method::Get;
    use iron::status::Status;
    use iron::headers::HttpDate;
    use hyper::header::{IfModifiedSince, CacheControl, CacheDirective, LastModified};
    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use iron_test::{mock, ProjectBuilder};
    use iron_test::mock::MockStream;
    use staticfile::Static;
    use std::io::Cursor;

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_cache_headers() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut stream = MockStream::new(Cursor::new("".to_string().into_bytes()));
        let mut reader = BufReader::new(&mut stream as &mut NetworkStream);
        let mut req = mock::request::new(Get,
                                         Url::parse("http://localhost:3000/file1.html").unwrap(),
                                         &mut reader);
        match st.handle(&mut req) {
            Ok(res) => {
                assert!(res.headers.get::<CacheControl>().is_some());
                assert!(res.headers.get::<LastModified>().is_some());
                let cache = res.headers.get::<CacheControl>().unwrap();
                let directives = vec![CacheDirective::Public, CacheDirective::MaxAge(2592000)];
                assert_eq!(*cache, CacheControl(directives));
            },
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_the_file_if_client_sends_no_modified_time() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut stream = MockStream::new(Cursor::new("".to_string().into_bytes()));
        let mut reader = BufReader::new(&mut stream as &mut NetworkStream);
        let mut req = mock::request::new(Get,
                                         Url::parse("http://localhost:3000/file1.html").unwrap(),
                                         &mut reader);
        match st.handle(&mut req) {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::Ok),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_the_file_if_client_has_old_version() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut stream = MockStream::new(Cursor::new("".to_string().into_bytes()));
        let mut reader = BufReader::new(&mut stream as &mut NetworkStream);
        let mut req = mock::request::new(Get,
                                         Url::parse("http://localhost:3000/file1.html").unwrap(),
                                         &mut reader);

        let now = time::get_time();
        let one_hour_ago = Timespec::new(now.sec - 3600, now.nsec);
        req.headers.set(IfModifiedSince(HttpDate(time::at(one_hour_ago))));
        match st.handle(&mut req) {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::Ok),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_304_if_client_has_file_cached() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut stream = MockStream::new(Cursor::new("".to_string().into_bytes()));
        let mut reader = BufReader::new(&mut stream as &mut NetworkStream);
        let mut req = mock::request::new(Get,
                                         Url::parse("http://localhost:3000/file1.html").unwrap(),
                                         &mut reader);
        req.headers.set(IfModifiedSince(HttpDate(time::now_utc())));
        match st.handle(&mut req) {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::NotModified),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_cache_index_html_for_directory_path() {
        let p = ProjectBuilder::new("example").file("dir/index.html", "this is index");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut stream = MockStream::new(Cursor::new("".to_string().into_bytes()));
        let mut reader = BufReader::new(&mut stream as &mut NetworkStream);
        let mut req = mock::request::new(Get,
                                         Url::parse("http://localhost:3000/dir/").unwrap(),
                                         &mut reader);
        req.headers.set(IfModifiedSince(HttpDate(time::now_utc())));
        match st.handle(&mut req) {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::NotModified),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_defer_to_static_handler_if_directory_misses_trailing_slash() {
        let p = ProjectBuilder::new("example").file("dir/index.html", "this is index");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut stream = MockStream::new(Cursor::new("".to_string().into_bytes()));
        let mut reader = BufReader::new(&mut stream as &mut NetworkStream);
        let mut req = mock::request::new(Get,
                                         Url::parse("http://localhost:3000/dir").unwrap(),
                                         &mut reader);
        req.headers.set(IfModifiedSince(HttpDate(time::now_utc())));
        match st.handle(&mut req) {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::MovedPermanently);
                assert!(res.headers.get::<LastModified>().is_none());
            },
            Err(e) => panic!("{}", e)
        }
    }
}

<commit_msg>(fix) updates cache tests to use new iron_test API<commit_after>extern crate time;

extern crate hyper;
extern crate iron;
extern crate iron_test;
extern crate staticfile;

#[cfg(feature = "cache")]
mod cache {
    use time;
    use time::{Timespec};

    #[cfg(feature = "cache")]
    use std::time::Duration;

    use iron::Headers;
    use iron::status::Status;
    use iron::headers::HttpDate;
    use hyper::header::{IfModifiedSince, CacheControl, CacheDirective, LastModified};
    use iron_test::ProjectBuilder;
    use iron_test::request;
    use staticfile::Static;

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_cache_headers() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let iron_res = request::get("http://localhost:3000/file1.html", Headers::new(), &st);

        match iron_res {
            Ok(res) => {
                assert!(res.headers.get::<CacheControl>().is_some());
                assert!(res.headers.get::<LastModified>().is_some());
                let cache = res.headers.get::<CacheControl>().unwrap();
                let directives = vec![CacheDirective::Public, CacheDirective::MaxAge(2592000)];
                assert_eq!(*cache, CacheControl(directives));
            },
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_the_file_if_client_sends_no_modified_time() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let iron_res = request::get("http://localhost:3000/file1.html", Headers::new(), &st);

        match iron_res {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::Ok),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_the_file_if_client_has_old_version() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));

        let now = time::get_time();
        let one_hour_ago = Timespec::new(now.sec - 3600, now.nsec);
        let mut headers = Headers::new();
        headers.set(IfModifiedSince(HttpDate(time::at(one_hour_ago))));
        let iron_res = request::get("http://localhost:3000/file1.html", headers, &st);

        match iron_res {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::Ok),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_return_304_if_client_has_file_cached() {
        let p = ProjectBuilder::new("example").file("file1.html", "this is file1");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut headers = Headers::new();
        headers.set(IfModifiedSince(HttpDate(time::now_utc())));
        let iron_res = request::get("http://localhost:3000/file1.html", headers, &st);

        match iron_res {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::NotModified),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_cache_index_html_for_directory_path() {
        let p = ProjectBuilder::new("example").file("dir/index.html", "this is index");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut headers = Headers::new();
        headers.set(IfModifiedSince(HttpDate(time::now_utc())));
        let iron_res = request::get("http://localhost:3000/dir/", headers, &st);

        match iron_res {
            Ok(res) => assert_eq!(res.status.unwrap(), Status::NotModified),
            Err(e) => panic!("{}", e)
        }
    }

    #[cfg(feature = "cache")]
    #[test]
    fn it_should_defer_to_static_handler_if_directory_misses_trailing_slash() {
        let p = ProjectBuilder::new("example").file("dir/index.html", "this is index");
        p.build();

        let st = Static::new(p.root().clone()).cache(Duration::from_secs(30*24*60*60));
        let mut headers = Headers::new();
        headers.set(IfModifiedSince(HttpDate(time::now_utc())));
        let iron_res = request::get("http://localhost:3000/dir", headers, &st);

        match iron_res {
            Ok(res) => {
                assert_eq!(res.status.unwrap(), Status::MovedPermanently);
                assert!(res.headers.get::<LastModified>().is_none());
            },
            Err(e) => panic!("{}", e)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Hooks for for the RPC system.
//!
//! Roughly corresponds to capability.h in the C++ implementation.

use {any_pointer, Error, MessageSize};
use traits::{Pipelined, Owned};
use private::capability::{ClientHook, ParamsHook, RequestHook, ResponseHook, ResultsHook};

#[cfg(feature = "rpc")]
use futures::Future;
#[cfg(feature = "rpc_try")]
use std::ops::Try;

use std::marker::PhantomData;

/// A computation that might eventually resolve to a value of type `T` or to an error
///  of type `E`. Dropping the promise cancels the computation.
#[must_use = "futures do nothing unless polled"]
pub struct Promise<T, E> {
    #[allow(dead_code)]
    inner: PromiseInner<T, E>,
}

enum PromiseInner<T, E> {
    Immediate(Result<T,E>),

    #[cfg(feature = "rpc")]
    Deferred(Box<Future<Item=T,Error=E> + 'static>),

    #[cfg(feature = "rpc")]
    Empty,
}

impl <T, E> Promise<T, E> {
    pub fn ok(value: T) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Ok(value)) }
    }

    pub fn err(error: E) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Err(error)) }
    }

    #[cfg(feature = "rpc")]
    pub fn from_future<F>(f: F) -> Promise<T, E>
        where F: Future<Item=T,Error=E> + 'static
    {
        Promise { inner: PromiseInner::Deferred(Box::new(f)) }
    }
}

#[cfg(feature = "rpc")]
impl <T, E> Future for Promise<T, E>
{
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> ::futures::Poll<Self::Item, Self::Error> {
        match self.inner {
            PromiseInner::Empty => panic!("Promise polled after done."),
            ref mut imm @ PromiseInner::Immediate(_) => {
                match ::std::mem::replace(imm, PromiseInner::Empty) {
                    PromiseInner::Immediate(Ok(v)) => Ok(::futures::Async::Ready(v)),
                    PromiseInner::Immediate(Err(e)) => Err(e),
                    _ => unreachable!(),
                }
            }
            PromiseInner::Deferred(ref mut f) => f.poll(),
        }
    }
}

#[cfg(feature = "rpc_try")]
impl<T> Try for Promise<T, crate::Error> {
    type Ok = T;
    type Error = crate::Error;

    fn into_result(mut self) -> Result<Self::Ok, Self::Error> {
        match self.poll() {
            Ok(::futures::Async::Ready(v)) => Ok(v),
            Ok(::futures::Async::NotReady) => Err(crate::Error::unimplemented("not ready".to_string())),
            Err(e) => Err(e)
        }
    }

    fn from_error(v: Self::Error) -> Self {
        Promise::err(v)
    }
    fn from_ok(v: Self::Ok) -> Self {
        Promise::ok(v)
    }
}

/// A promise for a result from a method call.
#[must_use]
pub struct RemotePromise<Results> where Results: Pipelined + for<'a> Owned<'a> + 'static {
    pub promise: Promise<Response<Results>, ::Error>,
    pub pipeline: Results::Pipeline,
}

/// A response from a method call, as seen by the client.
pub struct Response<Results> {
    pub marker: PhantomData<Results>,
    pub hook: Box<ResponseHook>,
}

impl <Results> Response<Results>
    where Results: Pipelined + for<'a> Owned<'a>
{
    pub fn new(hook: Box<ResponseHook>) -> Response<Results> {
        Response { marker: PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<Results as Owned<'a>>::Reader> {
        try!(self.hook.get()).get_as()
    }
}

/// A method call that has not been sent yet.
pub struct Request<Params, Results> {
    pub marker: PhantomData<(Params, Results)>,
    pub hook: Box<RequestHook>
}

impl <Params, Results> Request<Params, Results>
    where Params: for<'a> Owned<'a>
{
    pub fn new(hook: Box<RequestHook>) -> Request <Params, Results> {
        Request { hook: hook, marker: PhantomData }
    }

    pub fn get<'a>(&'a mut self) -> <Params as Owned<'a>>::Builder {
        self.hook.get().get_as().unwrap()
    }

    pub fn set(&mut self, from: <Params as Owned>::Reader) -> ::Result<()> {
        self.hook.get().set_as(from)
    }
}

#[cfg(feature = "rpc")]
impl <Params, Results> Request <Params, Results>
where Results: Pipelined + for<'a> Owned<'a> + 'static,
      <Results as Pipelined>::Pipeline: FromTypelessPipeline
{
    pub fn send(self) -> RemotePromise<Results> {
        let RemotePromise {promise, pipeline, ..} = self.hook.send();
        let typed_promise = Promise::from_future(promise.map(|response| {
            Response {hook: response.hook,
                      marker: PhantomData}
        }));
        RemotePromise { promise: typed_promise,
                        pipeline: FromTypelessPipeline::new(pipeline)
                      }
    }
}

/// The values of the parameters passed to a method call, as seen by the server.
pub struct Params<T> {
    pub marker: PhantomData<T>,
    pub hook: Box<ParamsHook>,
}

impl <T> Params <T> {
    pub fn new(hook: Box<ParamsHook>) -> Params<T> {
        Params { marker: PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<T as Owned<'a>>::Reader>
        where T: Owned<'a>
    {
        Ok(try!(try!(self.hook.get()).get_as()))
    }
}

/// The return values of a method, written in-place by the method body.
pub struct Results<T> {
    pub marker: PhantomData<T>,
    pub hook: Box<ResultsHook>,
}

impl <T> Results<T>
    where T: for<'a> Owned<'a>
{
    pub fn new(hook: Box<ResultsHook>) -> Results<T> {
        Results { marker: PhantomData, hook: hook }
    }

    pub fn get<'a>(&'a mut self) -> <T as Owned<'a>>::Builder {
        self.hook.get().unwrap().get_as().unwrap()
    }

    pub fn set(&mut self, other: <T as Owned>::Reader) -> ::Result<()>
    {
        self.hook.get().unwrap().set_as(other)
    }
}

pub trait FromTypelessPipeline {
    fn new (typeless: any_pointer::Pipeline) -> Self;
}

pub trait FromClientHook {
    fn new(Box<ClientHook>) -> Self;
}

/// An untyped client.
pub struct Client {
    pub hook: Box<ClientHook>
}

impl Client {
    pub fn new(hook: Box<ClientHook>) -> Client {
        Client { hook : hook }
    }

    pub fn new_call<Params, Results>(&self,
                                     interface_id : u64,
                                     method_id : u16,
                                     size_hint : Option<MessageSize>)
                                     -> Request<Params, Results> {
        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
        Request { hook: typeless.hook, marker: PhantomData }
    }

    /// If the capability is actually only a promise, the returned promise resolves once the
    /// capability itself has resolved to its final destination (or propagates the exception if
    /// the capability promise is rejected).  This is mainly useful for error-checking in the case
    /// where no calls are being made.  There is no reason to wait for this before making calls; if
    /// the capability does not resolve, the call results will propagate the error.
    #[cfg(feature = "rpc")]
    pub fn when_resolved(&self) -> Promise<(), Error> {
        self.hook.when_resolved()
    }
}

/// An untyped server.
pub trait Server {
    fn dispatch_call(&mut self, interface_id: u64, method_id: u16,
                     params: Params<any_pointer::Owned>,
                     results: Results<any_pointer::Owned>)
                     -> Promise<(), Error>;
}

<commit_msg>Unimplement into_result<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! Hooks for for the RPC system.
//!
//! Roughly corresponds to capability.h in the C++ implementation.

use {any_pointer, Error, MessageSize};
use traits::{Pipelined, Owned};
use private::capability::{ClientHook, ParamsHook, RequestHook, ResponseHook, ResultsHook};

#[cfg(feature = "rpc")]
use futures::Future;
#[cfg(feature = "rpc_try")]
use std::ops::Try;

use std::marker::PhantomData;

/// A computation that might eventually resolve to a value of type `T` or to an error
///  of type `E`. Dropping the promise cancels the computation.
#[must_use = "futures do nothing unless polled"]
pub struct Promise<T, E> {
    #[allow(dead_code)]
    inner: PromiseInner<T, E>,
}

enum PromiseInner<T, E> {
    Immediate(Result<T,E>),

    #[cfg(feature = "rpc")]
    Deferred(Box<Future<Item=T,Error=E> + 'static>),

    #[cfg(feature = "rpc")]
    Empty,
}

impl <T, E> Promise<T, E> {
    pub fn ok(value: T) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Ok(value)) }
    }

    pub fn err(error: E) -> Promise<T, E> {
        Promise { inner: PromiseInner::Immediate(Err(error)) }
    }

    #[cfg(feature = "rpc")]
    pub fn from_future<F>(f: F) -> Promise<T, E>
        where F: Future<Item=T,Error=E> + 'static
    {
        Promise { inner: PromiseInner::Deferred(Box::new(f)) }
    }
}

#[cfg(feature = "rpc")]
impl <T, E> Future for Promise<T, E>
{
    type Item = T;
    type Error = E;

    fn poll(&mut self) -> ::futures::Poll<Self::Item, Self::Error> {
        match self.inner {
            PromiseInner::Empty => panic!("Promise polled after done."),
            ref mut imm @ PromiseInner::Immediate(_) => {
                match ::std::mem::replace(imm, PromiseInner::Empty) {
                    PromiseInner::Immediate(Ok(v)) => Ok(::futures::Async::Ready(v)),
                    PromiseInner::Immediate(Err(e)) => Err(e),
                    _ => unreachable!(),
                }
            }
            PromiseInner::Deferred(ref mut f) => f.poll(),
        }
    }
}

#[cfg(feature = "rpc_try")]
impl<T> Try for Promise<T, crate::Error> {
    type Ok = T;
    type Error = crate::Error;

    fn into_result(mut self) -> Result<Self::Ok, Self::Error> {
        unimplemented!();
    }

    fn from_error(v: Self::Error) -> Self {
        Promise::err(v)
    }
    fn from_ok(v: Self::Ok) -> Self {
        Promise::ok(v)
    }
}

/// A promise for a result from a method call.
#[must_use]
pub struct RemotePromise<Results> where Results: Pipelined + for<'a> Owned<'a> + 'static {
    pub promise: Promise<Response<Results>, ::Error>,
    pub pipeline: Results::Pipeline,
}

/// A response from a method call, as seen by the client.
pub struct Response<Results> {
    pub marker: PhantomData<Results>,
    pub hook: Box<ResponseHook>,
}

impl <Results> Response<Results>
    where Results: Pipelined + for<'a> Owned<'a>
{
    pub fn new(hook: Box<ResponseHook>) -> Response<Results> {
        Response { marker: PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<Results as Owned<'a>>::Reader> {
        try!(self.hook.get()).get_as()
    }
}

/// A method call that has not been sent yet.
pub struct Request<Params, Results> {
    pub marker: PhantomData<(Params, Results)>,
    pub hook: Box<RequestHook>
}

impl <Params, Results> Request<Params, Results>
    where Params: for<'a> Owned<'a>
{
    pub fn new(hook: Box<RequestHook>) -> Request <Params, Results> {
        Request { hook: hook, marker: PhantomData }
    }

    pub fn get<'a>(&'a mut self) -> <Params as Owned<'a>>::Builder {
        self.hook.get().get_as().unwrap()
    }

    pub fn set(&mut self, from: <Params as Owned>::Reader) -> ::Result<()> {
        self.hook.get().set_as(from)
    }
}

#[cfg(feature = "rpc")]
impl <Params, Results> Request <Params, Results>
where Results: Pipelined + for<'a> Owned<'a> + 'static,
      <Results as Pipelined>::Pipeline: FromTypelessPipeline
{
    pub fn send(self) -> RemotePromise<Results> {
        let RemotePromise {promise, pipeline, ..} = self.hook.send();
        let typed_promise = Promise::from_future(promise.map(|response| {
            Response {hook: response.hook,
                      marker: PhantomData}
        }));
        RemotePromise { promise: typed_promise,
                        pipeline: FromTypelessPipeline::new(pipeline)
                      }
    }
}

/// The values of the parameters passed to a method call, as seen by the server.
pub struct Params<T> {
    pub marker: PhantomData<T>,
    pub hook: Box<ParamsHook>,
}

impl <T> Params <T> {
    pub fn new(hook: Box<ParamsHook>) -> Params<T> {
        Params { marker: PhantomData, hook: hook }
    }
    pub fn get<'a>(&'a self) -> ::Result<<T as Owned<'a>>::Reader>
        where T: Owned<'a>
    {
        Ok(try!(try!(self.hook.get()).get_as()))
    }
}

/// The return values of a method, written in-place by the method body.
pub struct Results<T> {
    pub marker: PhantomData<T>,
    pub hook: Box<ResultsHook>,
}

impl <T> Results<T>
    where T: for<'a> Owned<'a>
{
    pub fn new(hook: Box<ResultsHook>) -> Results<T> {
        Results { marker: PhantomData, hook: hook }
    }

    pub fn get<'a>(&'a mut self) -> <T as Owned<'a>>::Builder {
        self.hook.get().unwrap().get_as().unwrap()
    }

    pub fn set(&mut self, other: <T as Owned>::Reader) -> ::Result<()>
    {
        self.hook.get().unwrap().set_as(other)
    }
}

pub trait FromTypelessPipeline {
    fn new (typeless: any_pointer::Pipeline) -> Self;
}

pub trait FromClientHook {
    fn new(Box<ClientHook>) -> Self;
}

/// An untyped client.
pub struct Client {
    pub hook: Box<ClientHook>
}

impl Client {
    pub fn new(hook: Box<ClientHook>) -> Client {
        Client { hook : hook }
    }

    pub fn new_call<Params, Results>(&self,
                                     interface_id : u64,
                                     method_id : u16,
                                     size_hint : Option<MessageSize>)
                                     -> Request<Params, Results> {
        let typeless = self.hook.new_call(interface_id, method_id, size_hint);
        Request { hook: typeless.hook, marker: PhantomData }
    }

    /// If the capability is actually only a promise, the returned promise resolves once the
    /// capability itself has resolved to its final destination (or propagates the exception if
    /// the capability promise is rejected).  This is mainly useful for error-checking in the case
    /// where no calls are being made.  There is no reason to wait for this before making calls; if
    /// the capability does not resolve, the call results will propagate the error.
    #[cfg(feature = "rpc")]
    pub fn when_resolved(&self) -> Promise<(), Error> {
        self.hook.when_resolved()
    }
}

/// An untyped server.
pub trait Server {
    fn dispatch_call(&mut self, interface_id: u64, method_id: u16,
                     params: Params<any_pointer::Owned>,
                     results: Results<any_pointer::Owned>)
                     -> Promise<(), Error>;
}

<|endoftext|>


<commit_before>use crate::utils::span_lint_and_sugg;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for fields in struct literals where shorthands
    /// could be used.
    ///
    /// **Why is this bad?** If the field and variable names are the same,
    /// the field name is redundant.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let bar: u8 = 123;
    ///
    /// struct Foo {
    ///     bar: u8,
    /// }
    ///
    /// let foo = Foo { bar: bar };
    /// ```
    /// the last line can be simplified to
    /// ```ignore
    /// let foo = Foo { bar };
    /// ```
    pub REDUNDANT_FIELD_NAMES,
    style,
    "checks for fields in struct literals where shorthands could be used"
}

declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);

impl EarlyLintPass for RedundantFieldNames {
    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if let ExprKind::Struct(_, ref fields, _) = expr.kind {
            for field in fields {
                if field.is_shorthand {
                    continue;
                }
                if let ExprKind::Path(None, path) = &field.expr.kind {
                    if path.segments.len() == 1
                        && path.segments[0].ident == field.ident
                        && path.segments[0].args.is_none()
                    {
                        span_lint_and_sugg(
                            cx,
                            REDUNDANT_FIELD_NAMES,
                            field.span,
                            "redundant field names in struct initialization",
                            "replace it with",
                            field.ident.to_string(),
                            Applicability::MachineApplicable,
                        );
                    }
                }
            }
        }
    }
}
<commit_msg>Fix fallout in redundant_field_names<commit_after>use crate::utils::span_lint_and_sugg;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for fields in struct literals where shorthands
    /// could be used.
    ///
    /// **Why is this bad?** If the field and variable names are the same,
    /// the field name is redundant.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let bar: u8 = 123;
    ///
    /// struct Foo {
    ///     bar: u8,
    /// }
    ///
    /// let foo = Foo { bar: bar };
    /// ```
    /// the last line can be simplified to
    /// ```ignore
    /// let foo = Foo { bar };
    /// ```
    pub REDUNDANT_FIELD_NAMES,
    style,
    "checks for fields in struct literals where shorthands could be used"
}

declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);

impl EarlyLintPass for RedundantFieldNames {
    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if in_external_macro(cx.sess, expr.span) {
            return;
        }
        if let ExprKind::Struct(_, ref fields, _) = expr.kind {
            for field in fields {
                if field.is_shorthand {
                    continue;
                }
                if let ExprKind::Path(None, path) = &field.expr.kind {
                    if path.segments.len() == 1
                        && path.segments[0].ident == field.ident
                        && path.segments[0].args.is_none()
                    {
                        span_lint_and_sugg(
                            cx,
                            REDUNDANT_FIELD_NAMES,
                            field.span,
                            "redundant field names in struct initialization",
                            "replace it with",
                            field.ident.to_string(),
                            Applicability::MachineApplicable,
                        );
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate regex;

include!("../clap-test.rs");

use clap::{App, SubCommand, ErrorKind, Arg};

static HELP: &'static str = "clap-test v1.4.8
Kevin K. <kbknapp@gmail.com>
tests clap library

USAGE:
    clap-test [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]

FLAGS:
    -f, --flag       tests flags
    -F               tests flags with exclusions
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -O, --Option <option3>           specific vals [values: fast, slow]
        --long-option-2 <option2>    tests long options with exclusions
        --maxvals3 <maxvals>...      Tests 3 max vals
        --minvals2 <minvals>...      Tests 2 min vals
        --multvals <one> <two>       Tests mutliple values, not mult occs
        --multvalsmo <one> <two>     Tests mutliple values, and mult occs
    -o, --option <opt>...            tests options

ARGS:
    <positional>        tests positionals
    <positional2>       tests positionals with exclusions
    <positional3>...    tests specific values [values: vi, emacs]

SUBCOMMANDS:
    help      Prints this message or the help of the given subcommand(s)
    subcmd    tests subcommands";

static AFTER_HELP: &'static str = "some text that comes before the help

clap-test v1.4.8
tests clap library

USAGE:
    clap-test

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

some text that comes after the help";

static SC_HELP: &'static str = "subcmd 0.1
Kevin K. <kbknapp@gmail.com>
tests subcommands

USAGE:
    subcmd [FLAGS] [OPTIONS] [--] [scpositional]

FLAGS:
    -f, --flag    tests flags

OPTIONS:
    -o, --option <scoption>...    tests options

ARGS:
    <scpositional>    tests positionals";

static MULTI_SC_HELP: &'static str = "ctest-subcmd-multi 0.1
Kevin K. <kbknapp@gmail.com>
tests subcommands

USAGE:
    ctest subcmd multi [FLAGS] [OPTIONS]

FLAGS:
    -f, --flag       tests flags
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --option <scoption>...    tests options";

static ISSUE_626_CUTOFF: &'static str = "ctest 0.1

USAGE:
    ctest [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -c, --cafe <FILE>    A coffeehouse, coffee shop, or café is an
                         establishment which primarily serves hot
                         coffee, related coffee beverages (e.g., café
                         latte, cappuccino, espresso), tea, and other
                         hot beverages. Some coffeehouses also serve cold
                         beverages such as iced coffee and iced tea. Many
                         cafés also serve some type of food, such as light
                         snacks, muffins, or pastries.";

static ISSUE_626_PANIC: &'static str = "ctest 0.1

USAGE:
    ctest [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -c, --cafe <FILE>    La culture du café est très
                         développée dans de
                         nombreux pays à climat chaud
                         d'Amérique, d'Afrique et
                         d'Asie, dans des plantations qui
                         sont cultivées pour les marchés
                         d'exportation. Le café est souvent
                         une contribution majeure aux
                         exportations des régions productrices.";

#[test]
fn help_short() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "-h"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_long() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "--help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_no_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::UnknownArgument);
}

#[test]
fn help_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .subcommand(SubCommand::with_name("test")
            .about("tests things")
            .arg_from_usage("-v --verbose 'with verbosity'"))
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn subcommand_short_help() {
    let m = test::complex_app()
        .get_matches_from_safe(vec!["clap-test", "subcmd", "-h"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn subcommand_long_help() {
    let m = test::complex_app()
        .get_matches_from_safe(vec!["clap-test", "subcmd", "--help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn subcommand_help_rev() {
    let m = test::complex_app()
        .get_matches_from_safe(vec!["clap-test", "help", "subcmd"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn complex_help_output() {
    test::check_help(test::complex_app(), HELP);
}

#[test]
fn after_and_before_help_output() {
    let app = App::new("clap-test")
        .version("v1.4.8")
        .about("tests clap library")
        .before_help("some text that comes before the help")
        .after_help("some text that comes after the help");
    test::check_help(app, AFTER_HELP);
}

#[test]
fn multi_level_sc_help() {
    let app = App::new("ctest")
        .subcommand(SubCommand::with_name("subcmd")
            .subcommand(SubCommand::with_name("multi")
                .about("tests subcommands")
                .author("Kevin K. <kbknapp@gmail.com>")
                .version("0.1")
                .args_from_usage("
                    -f, --flag                    'tests flags'
                    -o, --option [scoption]...    'tests options'
                ")));
    test::check_err_output(app, "ctest help subcmd multi", MULTI_SC_HELP, false);
}

#[test]
fn no_wrap_help() {
    let app = App::new("ctest")
        .set_term_width(0)
        .help(MULTI_SC_HELP);
    test::check_err_output(app, "ctest --help", MULTI_SC_HELP, false);
}

#[test]
fn complex_subcommand_help_output() {
    let mut a = test::complex_app();
    let _ = a.get_matches_from_safe_borrow(vec![""]);
    let sc = a.p.subcommands.iter().filter(|s| s.p.meta.name == "subcmd").next().unwrap();
    // Now we check the output of print_help()
    let mut help = vec![];
    sc.write_help(&mut help).ok().expect("failed to print help");
    assert_eq!(&*String::from_utf8(help).unwrap(), SC_HELP);
}

#[test]
fn issue_626_unicode_cutoff() {
    let app = App::new("ctest")
        .version("0.1")
        .set_term_width(70)
        .arg(Arg::with_name("cafe")
           .short("c")
           .long("cafe")
           .value_name("FILE")
           .help("A coffeehouse, coffee shop, or café is an establishment \
           which primarily serves hot coffee, related coffee beverages \
           (e.g., café latte, cappuccino, espresso), tea, and other hot \
           beverages. Some coffeehouses also serve cold beverages such as \
           iced coffee and iced tea. Many cafés also serve some type of \
           food, such as light snacks, muffins, or pastries.")
           .takes_value(true));
    test::check_err_output(app, "ctest --help", ISSUE_626_CUTOFF, false);
}

#[test]
fn issue_626_panic() {
    let app = App::new("ctest")
        .version("0.1")
        .set_term_width(53)
        .arg(Arg::with_name("cafe")
           .short("c")
           .long("cafe")
           .value_name("FILE")
           .help("La culture du café est très développée dans de nombreux pays à climat chaud d'Amérique, \
           d'Afrique et d'Asie, dans des plantations qui sont cultivées pour les marchés d'exportation. \
           Le café est souvent une contribution majeure aux exportations des régions productrices.")
           .takes_value(true));
    test::check_err_output(app, "ctest --help", ISSUE_626_PANIC, false);
}
<commit_msg>tests: updates tests to account for variable term widths and non-english chars<commit_after>extern crate clap;
extern crate regex;

include!("../clap-test.rs");

use clap::{App, SubCommand, ErrorKind, Arg};

static HELP: &'static str = "clap-test v1.4.8
Kevin K. <kbknapp@gmail.com>
tests clap library

USAGE:
    clap-test [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]

FLAGS:
    -f, --flag       tests flags
    -F               tests flags with exclusions
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -O, --Option <option3>           specific vals [values: fast, slow]
        --long-option-2 <option2>    tests long options with exclusions
        --maxvals3 <maxvals>...      Tests 3 max vals
        --minvals2 <minvals>...      Tests 2 min vals
        --multvals <one> <two>       Tests mutliple values, not mult occs
        --multvalsmo <one> <two>     Tests mutliple values, and mult occs
    -o, --option <opt>...            tests options

ARGS:
    <positional>        tests positionals
    <positional2>       tests positionals with exclusions
    <positional3>...    tests specific values [values: vi, emacs]

SUBCOMMANDS:
    help      Prints this message or the help of the given subcommand(s)
    subcmd    tests subcommands";

static AFTER_HELP: &'static str = "some text that comes before the help

clap-test v1.4.8
tests clap library

USAGE:
    clap-test

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

some text that comes after the help";

static SC_HELP: &'static str = "subcmd 0.1
Kevin K. <kbknapp@gmail.com>
tests subcommands

USAGE:
    subcmd [FLAGS] [OPTIONS] [--] [scpositional]

FLAGS:
    -f, --flag    tests flags

OPTIONS:
    -o, --option <scoption>...    tests options

ARGS:
    <scpositional>    tests positionals";

static MULTI_SC_HELP: &'static str = "ctest-subcmd-multi 0.1
Kevin K. <kbknapp@gmail.com>
tests subcommands

USAGE:
    ctest subcmd multi [FLAGS] [OPTIONS]

FLAGS:
    -f, --flag       tests flags
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -o, --option <scoption>...    tests options";

static ISSUE_626_CUTOFF: &'static str = "ctest 0.1

USAGE:
    ctest [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -c, --cafe <FILE>    A coffeehouse, coffee shop, or café is an
                         establishment which primarily serves hot
                         coffee, related coffee beverages (e.g.,
                         café latte, cappuccino, espresso), tea,
                         and other hot beverages. Some
                         coffeehouses also serve cold beverages
                         such as iced coffee and iced tea. Many
                         cafés also serve some type of food, such
                         as light snacks, muffins, or pastries.";

static ISSUE_626_PANIC: &'static str = "ctest 0.1

USAGE:
    ctest [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -c, --cafe <FILE>    La culture du café est
                         très développée dans de
                         nombreux pays à climat
                         chaud d'Amérique,
                         d'Afrique et d'Asie,
                         dans des plantations
                         qui sont cultivées pour
                         les marchés
                         d'exportation. Le café
                         est souvent une
                         contribution majeure
                         aux exportations des
                         régions productrices.";

#[test]
fn help_short() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "-h"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_long() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "--help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn help_no_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::UnknownArgument);
}

#[test]
fn help_subcommand() {
    let m = App::new("test")
        .author("Kevin K.")
        .about("tests stuff")
        .version("1.3")
        .subcommand(SubCommand::with_name("test")
            .about("tests things")
            .arg_from_usage("-v --verbose 'with verbosity'"))
        .get_matches_from_safe(vec!["myprog", "help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn subcommand_short_help() {
    let m = test::complex_app()
        .get_matches_from_safe(vec!["clap-test", "subcmd", "-h"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn subcommand_long_help() {
    let m = test::complex_app()
        .get_matches_from_safe(vec!["clap-test", "subcmd", "--help"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn subcommand_help_rev() {
    let m = test::complex_app()
        .get_matches_from_safe(vec!["clap-test", "help", "subcmd"]);

    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
}

#[test]
fn complex_help_output() {
    test::check_help(test::complex_app(), HELP);
}

#[test]
fn after_and_before_help_output() {
    let app = App::new("clap-test")
        .version("v1.4.8")
        .about("tests clap library")
        .before_help("some text that comes before the help")
        .after_help("some text that comes after the help");
    test::check_help(app, AFTER_HELP);
}

#[test]
fn multi_level_sc_help() {
    let app = App::new("ctest")
        .subcommand(SubCommand::with_name("subcmd")
            .subcommand(SubCommand::with_name("multi")
                .about("tests subcommands")
                .author("Kevin K. <kbknapp@gmail.com>")
                .version("0.1")
                .args_from_usage("
                    -f, --flag                    'tests flags'
                    -o, --option [scoption]...    'tests options'
                ")));
    test::check_err_output(app, "ctest help subcmd multi", MULTI_SC_HELP, false);
}

#[test]
fn no_wrap_help() {
    let app = App::new("ctest")
        .set_term_width(0)
        .help(MULTI_SC_HELP);
    test::check_err_output(app, "ctest --help", MULTI_SC_HELP, false);
}

#[test]
fn complex_subcommand_help_output() {
    let mut a = test::complex_app();
    let _ = a.get_matches_from_safe_borrow(vec![""]);
    let sc = a.p.subcommands.iter().filter(|s| s.p.meta.name == "subcmd").next().unwrap();
    // Now we check the output of print_help()
    let mut help = vec![];
    sc.write_help(&mut help).ok().expect("failed to print help");
    assert_eq!(&*String::from_utf8(help).unwrap(), SC_HELP);
}

#[test]
fn issue_626_unicode_cutoff() {
    let app = App::new("ctest")
        .version("0.1")
        .set_term_width(70)
        .arg(Arg::with_name("cafe")
           .short("c")
           .long("cafe")
           .value_name("FILE")
           .help("A coffeehouse, coffee shop, or café is an establishment \
           which primarily serves hot coffee, related coffee beverages \
           (e.g., café latte, cappuccino, espresso), tea, and other hot \
           beverages. Some coffeehouses also serve cold beverages such as \
           iced coffee and iced tea. Many cafés also serve some type of \
           food, such as light snacks, muffins, or pastries.")
           .takes_value(true));
    test::check_err_output(app, "ctest --help", ISSUE_626_CUTOFF, false);
}

#[test]
fn issue_626_panic() {
    let app = App::new("ctest")
        .version("0.1")
        .set_term_width(52)
        .arg(Arg::with_name("cafe")
           .short("c")
           .long("cafe")
           .value_name("FILE")
           .help("La culture du café est très développée dans de nombreux pays à climat chaud d'Amérique, \
           d'Afrique et d'Asie, dans des plantations qui sont cultivées pour les marchés d'exportation. \
           Le café est souvent une contribution majeure aux exportations des régions productrices.")
           .takes_value(true));
    test::check_err_output(app, "ctest --help", ISSUE_626_PANIC, false);
}

#[test]
fn issue_626_variable_panic() {
    for i in 10..320 {
        let _ = App::new("ctest")
            .version("0.1")
            .set_term_width(i)
            .arg(Arg::with_name("cafe")
               .short("c")
               .long("cafe")
               .value_name("FILE")
               .help("La culture du café est très développée dans de nombreux pays à climat chaud d'Amérique, \
               d'Afrique et d'Asie, dans des plantations qui sont cultivées pour les marchés d'exportation. \
               Le café est souvent une contribution majeure aux exportations des régions productrices.")
               .takes_value(true))
            .get_matches_from_safe(vec!["ctest", "--help"]);
    }
}
<|endoftext|>


<commit_before>
extern crate nccl;

use nccl::{Pair, Value, Scanner};

#[test]
fn pair_new() {
    assert_eq!(Pair::new("hello"), Pair {
        key: "hello".into(),
        value: vec![]
    });
}

#[test]
fn pair_add() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p, Pair {
        key: "top_level".into(),
        value: vec![Pair {
            key: "key".into(),
            value: vec![]
        }]
    });
}

#[test]
fn pair_get() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p.get("key").unwrap(), &mut Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_index() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p["key"], Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_keys() {
    let mut p = Pair::new("top");
    p.add("numbers");
    p["numbers"].add("1");
    p["numbers"].add("2");
    p["numbers"].add("3");
    p["numbers"].add("4");
    p["numbers"].add("5");
    assert_eq!(p["numbers"].keys(), vec!["1", "2", "3", "4", "5"]);
}

#[test]
fn pair_value_parse() {
    let mut p = Pair::new("top");
    p.add("bools");
    p["bools"].add("true");
    assert!(p["bools"].value_as::<bool>().unwrap());
}

#[test]
fn scanner() {
    let mut s = Scanner::new(r#"key
                                 value"#.into());
    println!("{:?}", s.scan_tokens());
    panic!();
}

#[test]
fn readme() {
    let mut config = Pair::new("top_level");

    // server
    //     domain
    //         example.com
    //         www.example.com
    //     port
    //         80
    //         443
    //     root
    //         /var/www/html

    config.add("server");
    config["server"].add("domain");
    config["server"].add("port");
    config["server"].add("root");
    config["server"]["domain"].add("example.com");
    config["server"]["domain"].add("www.example.com");
    config["server"]["port"].add("80");
    config["server"]["port"].add("443");
    config["server"]["root"].add("/var/www/html");

    let ports = config["server"]["port"].keys_as::<i32>().unwrap();
    assert_eq!(ports, vec![80, 443]);
}

<commit_msg>update tests<commit_after>
extern crate nccl;

use nccl::{Pair, Value, Scanner};

#[test]
fn pair_new() {
    assert_eq!(Pair::new("hello"), Pair {
        key: "hello".into(),
        value: vec![]
    });
}

#[test]
fn pair_add() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p, Pair {
        key: "top_level".into(),
        value: vec![Pair {
            key: "key".into(),
            value: vec![]
        }]
    });
}

#[test]
fn pair_get() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p.get("key").unwrap(), &mut Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_index() {
    let mut p = Pair::new("top_level");
    p.add("key");
    assert_eq!(p["key"], Pair {
        key: "key".into(),
        value: vec![]
    });
}

#[test]
fn pair_keys() {
    let mut p = Pair::new("top");
    p.add("numbers");
    p["numbers"].add("1");
    p["numbers"].add("2");
    p["numbers"].add("3");
    p["numbers"].add("4");
    p["numbers"].add("5");
    assert_eq!(p["numbers"].keys(), vec!["1", "2", "3", "4", "5"]);
}

#[test]
fn pair_value_parse() {
    let mut p = Pair::new("top");
    p.add("bools");
    p["bools"].add("true");
    assert!(p["bools"].value_as::<bool>().unwrap());
}

#[test]
fn scanner() {
    let mut s = Scanner::new("\"this: is neato\": burrito 64\n".into());
    println!("{:?}", s.scan_tokens());
    panic!();
}

#[test]
fn readme() {
    let mut config = Pair::new("top_level");

    // server
    //     domain
    //         example.com
    //         www.example.com
    //     port
    //         80
    //         443
    //     root
    //         /var/www/html

    config.add("server");
    config["server"].add("domain");
    config["server"].add("port");
    config["server"].add("root");
    config["server"]["domain"].add("example.com");
    config["server"]["domain"].add("www.example.com");
    config["server"]["port"].add("80");
    config["server"]["port"].add("443");
    config["server"]["root"].add("/var/www/html");

    let ports = config["server"]["port"].keys_as::<i32>().unwrap();
    assert_eq!(ports, vec![80, 443]);
}

<|endoftext|>