<commit_before>#![cfg(test)]

use std::collections::HashMap;
use rustc_serialize::json;
use rustc_serialize::json::{Json, Object};
use rustc_serialize::{Decoder, Decodable};
use std::io::prelude::*;
use std::fs::File;
use token::{Token, ReservedWord};

pub struct ExpectPass {
    source: String,
    expected: Json,
    options: Option<Json>
}

pub struct ExpectFail {
    source: String,
    error: String,
    options: Option<Json>
}

pub enum TestCase {
    Pass(ExpectPass),
    Fail(ExpectFail)
}

trait JsonExt {
    fn into_array(self) -> json::Array;
    fn into_string(self) -> String;
    fn into_string_opt(self) -> Option<String>;
}

impl JsonExt for Json {
    fn into_array(self) -> json::Array {
        match self {
            Json::Array(array) => array,
            _ => panic!("expected array")
        }
    }
    fn into_string(self) -> String {
        match self {
            Json::String(string) => string,
            _ => panic!("expected string")
        }
    }
    fn into_string_opt(self) -> Option<String> {
        match self {
            Json::Null => None,
            Json::String(string) => Some(string),
            _ => panic!("expected string or null")
        }
    }
}

fn parse_expect_fail(test: &mut Object) -> ExpectFail {
    ExpectFail {
        source: test.remove("source").unwrap().into_string(),
        error: test.remove("error").unwrap().into_string(),
        options: None
    }
}

fn parse_expect_pass(test: &mut Object) -> ExpectPass {
    ExpectPass {
        source: test.remove("source").unwrap().into_string(),
        expected: test.remove("expected").unwrap(),
        options: None
    }
}

fn deserialize_reserved(word: &str) -> ReservedWord {
    match word {
        "Null"       => ReservedWord::Null,
        "True"       => ReservedWord::True,
        "False"      => ReservedWord::False,
        "Arguments"  => ReservedWord::Arguments,
        "Eval"       => ReservedWord::Eval,
        "Break"      => ReservedWord::Break,
        "Case"       => ReservedWord::Case,
        "Catch"      => ReservedWord::Catch,
        "Class"      => ReservedWord::Class,
        "Const"      => ReservedWord::Const,
        "Continue"   => ReservedWord::Continue,
        "Debugger"   => ReservedWord::Debugger,
        "Default"    => ReservedWord::Default,
        "Delete"     => ReservedWord::Delete,
        "Do"         => ReservedWord::Do,
        "Else"       => ReservedWord::Else,
        "Export"     => ReservedWord::Export,
        "Extends"    => ReservedWord::Extends,
        "Finally"    => ReservedWord::Finally,
        "For"        => ReservedWord::For,
        "Function"   => ReservedWord::Function,
        "If"         => ReservedWord::If,
        "Import"     => ReservedWord::Import,
        "In"         => ReservedWord::In,
        "Instanceof" => ReservedWord::Instanceof,
        "Let"        => ReservedWord::Let,
        "New"        => ReservedWord::New,
        "Return"     => ReservedWord::Return,
        "Static"     => ReservedWord::Static,
        "Super"      => ReservedWord::Super,
        "Switch"     => ReservedWord::Switch,
        "This"       => ReservedWord::This,
        "Throw"      => ReservedWord::Throw,
        "Try"        => ReservedWord::Try,
        "Typeof"     => ReservedWord::Typeof,
        "Var"        => ReservedWord::Var,
        "Void"       => ReservedWord::Void,
        "While"      => ReservedWord::While,
        "With"       => ReservedWord::With,
        "Yield"      => ReservedWord::Yield,
        "Enum"       => ReservedWord::Enum,
        "Await"      => ReservedWord::Await,
        "Implements" => ReservedWord::Implements,
        "Interface"  => ReservedWord::Interface,
        "Package"    => ReservedWord::Package,
        "Private"    => ReservedWord::Private,
        "Protected"  => ReservedWord::Protected,
        "Public"     => ReservedWord::Public,
        _            => panic!("invalid reserved word")
    }
}

fn deserialize_token(mut data: Json) -> Token {
    let mut arr = data.into_array();
    let ty = arr.remove(0).into_string();

    match &ty[..] {
        "Reserved"      => {
            let s = arr.remove(0).into_string();
            Token::Reserved(deserialize_reserved(&s[..]))
        }
        "LBrace"        => Token::LBrace,
        "RBrace"        => Token::RBrace,
        "LParen"        => Token::LParen,
        "RParen"        => Token::RParen,
        "LBrack"        => Token::LBrack,
        "RBrack"        => Token::RBrack,
        "Dot"           => Token::Dot,
        //"Ellipsis"    => Token::Ellipsis,
        "Semi"          => Token::Semi,
        "Comma"         => Token::Comma,
        "LAngle"        => Token::LAngle,
        "RAngle"        => Token::RAngle,
        "LEq"           => Token::LEq,
        "GEq"           => Token::GEq,
        "Eq"            => Token::Eq,
        "NEq"           => Token::NEq,
        "StrictEq"      => Token::StrictEq,
        "StrictNEq"     => Token::StrictNEq,
        "Plus"          => Token::Plus,
        "Minus"         => Token::Minus,
        "Star"          => Token::Star,
        "Mod"           => Token::Mod,
        "Slash"         => Token::Slash,
        "Inc"           => Token::Inc,
        "Dec"           => Token::Dec,
        "LShift"        => Token::LShift,
        "RShift"        => Token::RShift,
        "URShift"       => Token::URShift,
        "BitAnd"        => Token::BitAnd,
        "BitOr"         => Token::BitOr,
        "BitXor"        => Token::BitXor,
        "Bang"          => Token::Bang,
        "Tilde"         => Token::Tilde,
        "LogicalAnd"    => Token::LogicalAnd,
        "LogicalOr"     => Token::LogicalOr,
        "Question"      => Token::Question,
        "Colon"         => Token::Colon,
        "Assign"        => Token::Assign,
        "PlusAssign"    => Token::PlusAssign,
        "MinusAssign"   => Token::MinusAssign,
        "StarAssign"    => Token::StarAssign,
        "SlashAssign"   => Token::SlashAssign,
        "ModAssign"     => Token::ModAssign,
        "LShiftAssign"  => Token::LShiftAssign,
        "RShiftAssign"  => Token::RShiftAssign,
        "URShiftAssign" => Token::URShiftAssign,
        "BitAndAssign"  => Token::BitAndAssign,
        "BitOrAssign"   => Token::BitOrAssign,
        "BitXorAssign"  => Token::BitXorAssign,
        "Arrow"         => Token::Arrow,
        "Newline"       => Token::Newline,
        "EOF"           => Token::EOF,
        "DecimalInt"    => Token::DecimalInt(arr.remove(0).into_string()),
        "BinaryInt"     => {
            match arr[0..1] {
                [flag, value] => Token::BinaryInt(flag.into_string().remove(0),
                                                  value.into_string()),
                _ => panic!("invalid token")
            }
        }
        "OctalInt"      => {
            let flag = arr.remove(0).into_string_opt().map(|mut str| str.remove(0));
            Token::OctalInt(flag, arr.remove(0).into_string())
        }
        "HexInt"        => {
            let flag = arr.remove(0).into_string().remove(0);
            Token::HexInt(flag, arr.remove(0).into_string())
        }
        "Float"         => {
            let int = arr.remove(0).into_string_opt();
            let frac = arr.remove(0).into_string_opt();
            let exp = arr.remove(0).into_string_opt();
            Token::Float(int, frac, exp)
        }
        "String"        => Token::String(arr.remove(0).into_string()),
        "RegExp"        => Token::RegExp(arr.remove(0).into_string()),
        "Identifier"    => Token::Identifier(arr.remove(0).into_string()),
        "LineComment"   => Token::LineComment(arr.remove(0).into_string()),
        "BlockComment"  => Token::BlockComment(arr.remove(0).into_string()),
        _               => panic!("invalid token")
    }
}

fn parse_test(mut test: Json) -> TestCase {
    let obj = test.as_object_mut().unwrap();
    if obj.contains_key("error") {
        TestCase::Fail(parse_expect_fail(obj))
    } else {
        TestCase::Pass(parse_expect_pass(obj))
    }
}

pub fn parse_tests(src: &str) -> Vec<TestCase> {
    let t = Json::from_str("[\"DecimalInt\",\"11.3\"]").unwrap();
    println!("yo: {:?}", deserialize_token(t));
    let data: Json = src.parse().unwrap();
    data.into_array()
        .into_iter()
        .map(parse_test)
        .collect()
}
<commit_msg>Niko says there's no syntax for removing an array. abstracting as a macro but _ doesn't match a token tree for some reason src/test/mod.rs:204:48: 204:49 error: unexpected token: `_` src/test/mod.rs:204             let (flag, value) = tuplify!(arr, (_, _));                                                                    ^<commit_after>#![cfg(test)]

use std::collections::HashMap;
use rustc_serialize::json;
use rustc_serialize::json::{Json, Object};
use rustc_serialize::{Decoder, Decodable};
use std::io::prelude::*;
use std::fs::File;
use token::{Token, ReservedWord};

pub struct ExpectPass {
    source: String,
    expected: Json,
    options: Option<Json>
}

pub struct ExpectFail {
    source: String,
    error: String,
    options: Option<Json>
}

pub enum TestCase {
    Pass(ExpectPass),
    Fail(ExpectFail)
}

trait JsonExt {
    fn into_array(self) -> json::Array;
    fn into_string(self) -> String;
    fn into_string_opt(self) -> Option<String>;
}

impl JsonExt for Json {
    fn into_array(self) -> json::Array {
        match self {
            Json::Array(array) => array,
            _ => panic!("expected array")
        }
    }
    fn into_string(self) -> String {
        match self {
            Json::String(string) => string,
            _ => panic!("expected string")
        }
    }
    fn into_string_opt(self) -> Option<String> {
        match self {
            Json::Null => None,
            Json::String(string) => Some(string),
            _ => panic!("expected string or null")
        }
    }
}

fn parse_expect_fail(test: &mut Object) -> ExpectFail {
    ExpectFail {
        source: test.remove("source").unwrap().into_string(),
        error: test.remove("error").unwrap().into_string(),
        options: None
    }
}

fn parse_expect_pass(test: &mut Object) -> ExpectPass {
    ExpectPass {
        source: test.remove("source").unwrap().into_string(),
        expected: test.remove("expected").unwrap(),
        options: None
    }
}

fn deserialize_reserved(word: &str) -> ReservedWord {
    match word {
        "Null"       => ReservedWord::Null,
        "True"       => ReservedWord::True,
        "False"      => ReservedWord::False,
        "Arguments"  => ReservedWord::Arguments,
        "Eval"       => ReservedWord::Eval,
        "Break"      => ReservedWord::Break,
        "Case"       => ReservedWord::Case,
        "Catch"      => ReservedWord::Catch,
        "Class"      => ReservedWord::Class,
        "Const"      => ReservedWord::Const,
        "Continue"   => ReservedWord::Continue,
        "Debugger"   => ReservedWord::Debugger,
        "Default"    => ReservedWord::Default,
        "Delete"     => ReservedWord::Delete,
        "Do"         => ReservedWord::Do,
        "Else"       => ReservedWord::Else,
        "Export"     => ReservedWord::Export,
        "Extends"    => ReservedWord::Extends,
        "Finally"    => ReservedWord::Finally,
        "For"        => ReservedWord::For,
        "Function"   => ReservedWord::Function,
        "If"         => ReservedWord::If,
        "Import"     => ReservedWord::Import,
        "In"         => ReservedWord::In,
        "Instanceof" => ReservedWord::Instanceof,
        "Let"        => ReservedWord::Let,
        "New"        => ReservedWord::New,
        "Return"     => ReservedWord::Return,
        "Static"     => ReservedWord::Static,
        "Super"      => ReservedWord::Super,
        "Switch"     => ReservedWord::Switch,
        "This"       => ReservedWord::This,
        "Throw"      => ReservedWord::Throw,
        "Try"        => ReservedWord::Try,
        "Typeof"     => ReservedWord::Typeof,
        "Var"        => ReservedWord::Var,
        "Void"       => ReservedWord::Void,
        "While"      => ReservedWord::While,
        "With"       => ReservedWord::With,
        "Yield"      => ReservedWord::Yield,
        "Enum"       => ReservedWord::Enum,
        "Await"      => ReservedWord::Await,
        "Implements" => ReservedWord::Implements,
        "Interface"  => ReservedWord::Interface,
        "Package"    => ReservedWord::Package,
        "Private"    => ReservedWord::Private,
        "Protected"  => ReservedWord::Protected,
        "Public"     => ReservedWord::Public,
        _            => panic!("invalid reserved word")
    }
}

macro_rules! right {
    ( $l:expr, $r:expr ) => { $r }
}

macro_rules! tuplify {
    ( $v:expr, ( $($dummy:tt),* ) ) => {
        {
            let mut t = $v.into_iter();
            ($(
                right!($dummy, t.next().unwrap())
            ),*)
        }
    };
}

fn deserialize_token(mut data: Json) -> Token {
    let mut arr = data.into_array();
    let ty = arr.remove(0).into_string();

    match &ty[..] {
        "Reserved"      => {
            let s = arr.remove(0).into_string();
            Token::Reserved(deserialize_reserved(&s[..]))
        }
        "LBrace"        => Token::LBrace,
        "RBrace"        => Token::RBrace,
        "LParen"        => Token::LParen,
        "RParen"        => Token::RParen,
        "LBrack"        => Token::LBrack,
        "RBrack"        => Token::RBrack,
        "Dot"           => Token::Dot,
        //"Ellipsis"    => Token::Ellipsis,
        "Semi"          => Token::Semi,
        "Comma"         => Token::Comma,
        "LAngle"        => Token::LAngle,
        "RAngle"        => Token::RAngle,
        "LEq"           => Token::LEq,
        "GEq"           => Token::GEq,
        "Eq"            => Token::Eq,
        "NEq"           => Token::NEq,
        "StrictEq"      => Token::StrictEq,
        "StrictNEq"     => Token::StrictNEq,
        "Plus"          => Token::Plus,
        "Minus"         => Token::Minus,
        "Star"          => Token::Star,
        "Mod"           => Token::Mod,
        "Slash"         => Token::Slash,
        "Inc"           => Token::Inc,
        "Dec"           => Token::Dec,
        "LShift"        => Token::LShift,
        "RShift"        => Token::RShift,
        "URShift"       => Token::URShift,
        "BitAnd"        => Token::BitAnd,
        "BitOr"         => Token::BitOr,
        "BitXor"        => Token::BitXor,
        "Bang"          => Token::Bang,
        "Tilde"         => Token::Tilde,
        "LogicalAnd"    => Token::LogicalAnd,
        "LogicalOr"     => Token::LogicalOr,
        "Question"      => Token::Question,
        "Colon"         => Token::Colon,
        "Assign"        => Token::Assign,
        "PlusAssign"    => Token::PlusAssign,
        "MinusAssign"   => Token::MinusAssign,
        "StarAssign"    => Token::StarAssign,
        "SlashAssign"   => Token::SlashAssign,
        "ModAssign"     => Token::ModAssign,
        "LShiftAssign"  => Token::LShiftAssign,
        "RShiftAssign"  => Token::RShiftAssign,
        "URShiftAssign" => Token::URShiftAssign,
        "BitAndAssign"  => Token::BitAndAssign,
        "BitOrAssign"   => Token::BitOrAssign,
        "BitXorAssign"  => Token::BitXorAssign,
        "Arrow"         => Token::Arrow,
        "Newline"       => Token::Newline,
        "EOF"           => Token::EOF,
        "DecimalInt"    => Token::DecimalInt(arr.remove(0).into_string()),
        "BinaryInt"     => {
            let (flag, value) = tuplify!(arr, (_, _));
            Token::BinaryInt(flag.into_string().remove(0), value.into_string())
        }
        "OctalInt"      => {
            let flag = arr.remove(0).into_string_opt().map(|mut str| str.remove(0));
            Token::OctalInt(flag, arr.remove(0).into_string())
        }
        "HexInt"        => {
            let flag = arr.remove(0).into_string().remove(0);
            Token::HexInt(flag, arr.remove(0).into_string())
        }
        "Float"         => {
            let int = arr.remove(0).into_string_opt();
            let frac = arr.remove(0).into_string_opt();
            let exp = arr.remove(0).into_string_opt();
            Token::Float(int, frac, exp)
        }
        "String"        => Token::String(arr.remove(0).into_string()),
        "RegExp"        => Token::RegExp(arr.remove(0).into_string()),
        "Identifier"    => Token::Identifier(arr.remove(0).into_string()),
        "LineComment"   => Token::LineComment(arr.remove(0).into_string()),
        "BlockComment"  => Token::BlockComment(arr.remove(0).into_string()),
        _               => panic!("invalid token")
    }
}

fn parse_test(mut test: Json) -> TestCase {
    let obj = test.as_object_mut().unwrap();
    if obj.contains_key("error") {
        TestCase::Fail(parse_expect_fail(obj))
    } else {
        TestCase::Pass(parse_expect_pass(obj))
    }
}

pub fn parse_tests(src: &str) -> Vec<TestCase> {
    let t = Json::from_str("[\"DecimalInt\",\"11.3\"]").unwrap();
    println!("yo: {:?}", deserialize_token(t));
    let data: Json = src.parse().unwrap();
    data.into_array()
        .into_iter()
        .map(parse_test)
        .collect()
}
<|endoftext|>


<commit_before>//! Support for log4rs configuration from TOML files.
//!
//! # Syntax
//!
//! ```toml
//! # If set, log4rs will scan the file at the specified rate in seconds for
//! # changes and automatically reconfigure the logger.
//! refresh_rate = 30
//!
//! # Appenders are configured as tables inside the "appender" table. This
//! # appender is named "foo".
//! [appender.foo]
//! # All appenders must specify a "kind", which must match the kind of an
//! # appender mapping provided to the `Creator` used to deserialize the
//! # config file.
//! kind = "console"
//!
//! # Arbitrary fields may be added to appender configurations. Remaining
//! # entries will be passed to the `CreateAppender` object associated with
//! # the specified kind.
//! pattern = "%d [%t] %m"
//!
//! # Filters attached to an appender are configured inside the "filter" array.
//! [[appender.foo.filter]]
//! # Like appenders, filters must specify a "kind".
//! kind = "threshold"
//!
//! # Also like appenders, arbitrary fields may be added to filter
//! # configurations.
//! level = "error"
//!
//! # The root logger is configured by the "root" table. It is optional.
//! # If the "root" table is not specified, the root will default to a level of
//! # "debug" and no appenders.
//! [root]
//! # The maximum log level for the root logger. Must be specified if the
//! # "root" table is defined.
//! level = "warn"
//!
//! # The list of names of appenders attached to the root logger. If not
//! # specified, defaults to an empty list.
//! appenders = ["foo"]
//!
//! # Loggers are configured as tables inside of the "logger" array.
//! [[logger]]
//! # The name of the logger. Must be specified.
//! name = "foo::bar::baz"
//!
//! # The maximum . If it is not present, the level of the logger's parent is used.
//! level = "trace"
//!
//! # A list of names of appenders attached to the logger. If not specified,
//! # defaults to an empty list.
//! appenders = ["foo"]
//!
//! # The additivity of the logger. If true, the appenders attached to this
//! # logger's parent will also be attached to this logger. If not specified,
//! # defaults to true.
//! additive = false
//! ```
use log::LogLevelFilter;
use std::collections::HashMap;
use std::default::Default;
use std::error;
use std::fmt;
use std::time::Duration;
use toml_parser::{self, Value};

use appender::{FileAppender, ConsoleAppender};
use filter::{ThresholdFilter};
use config;
use pattern::PatternLayout;
use {Append, Filter, PrivateTomlConfigExt, PrivateConfigErrorsExt};

mod raw;

/// A trait implemented by types that can create appenders.
pub trait CreateAppender: Send + 'static {
    /// Creates an appender with the specified config.
    fn create_appender(&self, config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>>;
}

/// A trait implemented by types that can create filters.
pub trait CreateFilter: Send + 'static {
    /// Creates a filter with the specified config.
    fn create_filter(&self, config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>>;
}

/// A type that can create appenders.
///
/// `Creator` implements `Default`, which returns a `Creator` with the
/// following mappings:
///
/// * Appenders
///     * "file" -> `FileAppenderCreator`
///     * "console" -> `ConsoleAppenderCreator`
/// * Filters
///     * "threshold" -> `ThresholdFilterCreator`
pub struct Creator {
    appenders: HashMap<String, Box<CreateAppender>>,
    filters: HashMap<String, Box<CreateFilter>>,
}

impl Default for Creator {
    fn default() -> Creator {
        let mut creator = Creator::new();
        creator.add_appender("file", Box::new(FileAppenderCreator));
        creator.add_appender("console", Box::new(ConsoleAppenderCreator));
        creator.add_filter("threshold", Box::new(ThresholdFilterCreator));
        creator
    }
}

impl Creator {
    /// Creates a new `Creator` with no appender or filter mappings.
    pub fn new() -> Creator {
        Creator {
            appenders: HashMap::new(),
            filters: HashMap::new(),
        }
    }

    /// Adds a mapping from the specified `kind` to the specified appender
    /// creator.
    pub fn add_appender(&mut self, kind: &str, creator: Box<CreateAppender>) {
        self.appenders.insert(kind.to_string(), creator);
    }

    /// Adds a mapping from the specified `kind` to the specified filter
    /// creator.
    pub fn add_filter(&mut self, kind: &str, creator: Box<CreateFilter>) {
        self.filters.insert(kind.to_string(), creator);
    }

    fn create_appender(&self, kind: &str, config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        match self.appenders.get(kind) {
            Some(creator) => creator.create_appender(config),
            None => Err(Box::new(StringError(format!("No creator registered for appender kind \"{}\"", kind))))
        }
    }

    fn create_filter(&self, kind: &str, config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        match self.filters.get(kind) {
            Some(creator) => creator.create_filter(config),
            None => Err(Box::new(StringError(format!("No creator registered for filter kind \"{}\"", kind))))
        }
    }
}

/// Errors encountered when parsing a log4rs TOML config.
#[derive(Debug)]
pub struct ParseErrors {
    errors: Vec<String>,
}

impl fmt::Display for ParseErrors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for ParseErrors {
    fn description(&self) -> &str {
        "Errors encountered when parsing a log4rs TOML config"
    }
}

/// An error returned when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub enum Error {
    /// An error instantiating an appender.
    AppenderCreation(String, Box<error::Error>),
    /// An error instantiating a filter.
    FilterCreation(String, Box<error::Error>),
    /// An error when creating the log4rs `Config`.
    Config(config::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::AppenderCreation(ref appender, ref err) => {
                write!(fmt, "Error creating appender `{}`: {}", appender, err)
            }
            Error::FilterCreation(ref appender, ref err) => {
                write!(fmt, "Error creating filter for appender `{}`: {}", appender, err)
            }
            Error::Config(ref err) => write!(fmt, "Error creating config: {}", err),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "An error encountered when deserializing a TOML configuration into a log4rs `Config`"
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::AppenderCreation(_, ref err) => Some(&**err),
            Error::FilterCreation(_, ref err) => Some(&**err),
            Error::Config(ref err) => Some(err),
        }
    }
}

/// Errors encountered when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub struct Errors {
    errors: Vec<Error>,
}

impl Errors {
    /// Returns the list of errors encountered.
    pub fn errors(&self) -> &[Error] {
        &self.errors
    }
}

impl fmt::Display for Errors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for Errors {
    fn description(&self) -> &str {
        "Errors encountered when deserializing a TOML configuration into a log4rs `Config`"
    }
}

/// A deserialized TOML log4rs configuration.
pub struct Config {
    refresh_rate: Option<Duration>,
    config: config::Config,
}

impl Config {
    /// Creates a log4rs `Config` from the specified TOML config string and `Creator`.
    pub fn parse(config: &str, creator: &Creator)
                 -> Result<(Config, Result<(), Errors>), ParseErrors> {
        let mut errors = vec![];

        let config = match raw::parse(config) {
            Ok(config) => config,
            Err(errors) => return Err(ParseErrors { errors: errors }),
        };

        let raw::Config {
            refresh_rate,
            root: raw_root,
            appenders: raw_appenders,
            loggers: raw_loggers,
        } = config;

        let root = match raw_root {
            Some(raw_root) => {
                let mut root = config::Root::builder(raw_root.level);
                if let Some(appenders) = raw_root.appenders {
                    root = root.appenders(appenders);
                }
                root.build()
            }
            None => config::Root::builder(LogLevelFilter::Debug).build(),
        };

        let mut config = config::Config::builder(root);

        for (name, raw::Appender { kind, config: raw_config, filters }) in raw_appenders {
            match creator.create_appender(&kind, raw_config) {
                Ok(appender_obj) => {
                    let mut builder = config::Appender::builder(name.clone(), appender_obj);
                    for raw::Filter { kind, config } in filters.unwrap_or(vec![]) {
                        match creator.create_filter(&kind, config) {
                            Ok(filter) => builder = builder.filter(filter),
                            Err(err) => errors.push(Error::FilterCreation(name.clone(), err)),
                        }
                    }
                    config = config.appender(builder.build());
                }
                Err(err) => errors.push(Error::AppenderCreation(name, err)),
            }
        }

        for logger in raw_loggers {
            let raw::Logger { name, level, appenders, additive } = logger;
            let mut logger = config::Logger::builder(name, level);
            if let Some(appenders) = appenders {
                logger = logger.appenders(appenders);
            }
            if let Some(additive) = additive {
                logger = logger.additive(additive);
            }
            config = config.logger(logger.build());
        }

        let (config, config_errors) = config.build_lossy();
        if let Err(config_errors) = config_errors {
            for error in config_errors.unpack() {
                errors.push(Error::Config(error));
            }
        }

        let config = Config {
            refresh_rate: refresh_rate,
            config: config
        };

        let errors = if errors.is_empty() {
            Ok(())
        } else {
            Err(Errors {
                errors: errors
            })
        };

        Ok((config, errors))
    }

    /// Returns the requested refresh rate.
    pub fn refresh_rate(&self) -> Option<Duration> {
        self.refresh_rate
    }

    /// Returns the log4rs `Config`.
    pub fn config(&self) -> &config::Config {
        &self.config
    }
}

impl PrivateTomlConfigExt for Config {
    fn unpack(self) -> (Option<Duration>, config::Config) {
        let Config { refresh_rate, config } = self;
        (refresh_rate, config)
    }
}

#[derive(Debug)]
struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&self.0)
    }
}

impl error::Error for StringError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl error::FromError<String> for StringError {
    fn from_error(s: String) -> StringError {
        StringError(s)
    }
}

/// An appender creator for the `FileAppender`.
///
/// The `path` key is required, and specifies the path to the log file. The
/// `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output. The `append` key is optional and specifies whether the
/// output file should be truncated or appended to.
pub struct FileAppenderCreator;

impl CreateAppender for FileAppenderCreator {
    fn create_appender(&self, mut config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let path = match config.remove("path") {
            Some(Value::String(path)) => path,
            Some(_) => return Err(Box::new(StringError("`path` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`path` is required".to_string()))),
        };

        let mut appender = FileAppender::builder(&path);
        match config.remove("pattern") {
            Some(Value::String(pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(&pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        match config.remove("append") {
            Some(Value::Boolean(append)) => appender = appender.append(append),
            None => {}
            Some(_) => return Err(Box::new(StringError("`append` must be a bool".to_string()))),
        }

        match appender.build() {
            Ok(appender) => Ok(Box::new(appender)),
            Err(err) => Err(Box::new(err))
        }
    }
}

/// An appender creator for the `ConsoleAppender`.
///
/// The `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output.
pub struct ConsoleAppenderCreator;

impl CreateAppender for ConsoleAppenderCreator {
    fn create_appender(&self, mut config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let mut appender = ConsoleAppender::builder();
        match config.remove("pattern") {
            Some(Value::String(pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(&pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        Ok(Box::new(appender.build()))
    }
}

/// A filter creator for the `ThresholdFilter`.
///
/// The `level` key is required and specifies the threshold for the filter.
pub struct ThresholdFilterCreator;

impl CreateFilter for ThresholdFilterCreator {
    fn create_filter(&self, mut config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        let level = match config.remove("level") {
            Some(Value::String(level)) => level,
            Some(_) => return Err(Box::new(StringError("`level` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`level` must be provided".to_string()))),
        };

        let level = match level.parse() {
            Ok(level) => level,
            Err(_) => return Err(Box::new(StringError(format!("Invalid `level` \"{}\"", level)))),
        };

        Ok(Box::new(ThresholdFilter::new(level)))
    }
}
<commit_msg>Disallow unknown keys in creator configs<commit_after>//! Support for log4rs configuration from TOML files.
//!
//! # Syntax
//!
//! ```toml
//! # If set, log4rs will scan the file at the specified rate in seconds for
//! # changes and automatically reconfigure the logger.
//! refresh_rate = 30
//!
//! # Appenders are configured as tables inside the "appender" table. This
//! # appender is named "foo".
//! [appender.foo]
//! # All appenders must specify a "kind", which must match the kind of an
//! # appender mapping provided to the `Creator` used to deserialize the
//! # config file.
//! kind = "console"
//!
//! # Arbitrary fields may be added to appender configurations. Remaining
//! # entries will be passed to the `CreateAppender` object associated with
//! # the specified kind.
//! pattern = "%d [%t] %m"
//!
//! # Filters attached to an appender are configured inside the "filter" array.
//! [[appender.foo.filter]]
//! # Like appenders, filters must specify a "kind".
//! kind = "threshold"
//!
//! # Also like appenders, arbitrary fields may be added to filter
//! # configurations.
//! level = "error"
//!
//! # The root logger is configured by the "root" table. It is optional.
//! # If the "root" table is not specified, the root will default to a level of
//! # "debug" and no appenders.
//! [root]
//! # The maximum log level for the root logger. Must be specified if the
//! # "root" table is defined.
//! level = "warn"
//!
//! # The list of names of appenders attached to the root logger. If not
//! # specified, defaults to an empty list.
//! appenders = ["foo"]
//!
//! # Loggers are configured as tables inside of the "logger" array.
//! [[logger]]
//! # The name of the logger. Must be specified.
//! name = "foo::bar::baz"
//!
//! # The maximum . If it is not present, the level of the logger's parent is used.
//! level = "trace"
//!
//! # A list of names of appenders attached to the logger. If not specified,
//! # defaults to an empty list.
//! appenders = ["foo"]
//!
//! # The additivity of the logger. If true, the appenders attached to this
//! # logger's parent will also be attached to this logger. If not specified,
//! # defaults to true.
//! additive = false
//! ```
use log::LogLevelFilter;
use std::collections::HashMap;
use std::default::Default;
use std::error;
use std::fmt;
use std::time::Duration;
use toml_parser::{self, Value};

use appender::{FileAppender, ConsoleAppender};
use filter::{ThresholdFilter};
use config;
use pattern::PatternLayout;
use {Append, Filter, PrivateTomlConfigExt, PrivateConfigErrorsExt};

mod raw;

/// A trait implemented by types that can create appenders.
pub trait CreateAppender: Send + 'static {
    /// Creates an appender with the specified config.
    fn create_appender(&self, config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>>;
}

/// A trait implemented by types that can create filters.
pub trait CreateFilter: Send + 'static {
    /// Creates a filter with the specified config.
    fn create_filter(&self, config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>>;
}

/// A type that can create appenders.
///
/// `Creator` implements `Default`, which returns a `Creator` with the
/// following mappings:
///
/// * Appenders
///     * "file" -> `FileAppenderCreator`
///     * "console" -> `ConsoleAppenderCreator`
/// * Filters
///     * "threshold" -> `ThresholdFilterCreator`
pub struct Creator {
    appenders: HashMap<String, Box<CreateAppender>>,
    filters: HashMap<String, Box<CreateFilter>>,
}

impl Default for Creator {
    fn default() -> Creator {
        let mut creator = Creator::new();
        creator.add_appender("file", Box::new(FileAppenderCreator));
        creator.add_appender("console", Box::new(ConsoleAppenderCreator));
        creator.add_filter("threshold", Box::new(ThresholdFilterCreator));
        creator
    }
}

impl Creator {
    /// Creates a new `Creator` with no appender or filter mappings.
    pub fn new() -> Creator {
        Creator {
            appenders: HashMap::new(),
            filters: HashMap::new(),
        }
    }

    /// Adds a mapping from the specified `kind` to the specified appender
    /// creator.
    pub fn add_appender(&mut self, kind: &str, creator: Box<CreateAppender>) {
        self.appenders.insert(kind.to_string(), creator);
    }

    /// Adds a mapping from the specified `kind` to the specified filter
    /// creator.
    pub fn add_filter(&mut self, kind: &str, creator: Box<CreateFilter>) {
        self.filters.insert(kind.to_string(), creator);
    }

    fn create_appender(&self, kind: &str, config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        match self.appenders.get(kind) {
            Some(creator) => creator.create_appender(config),
            None => Err(Box::new(StringError(format!("No creator registered for appender kind \"{}\"", kind))))
        }
    }

    fn create_filter(&self, kind: &str, config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        match self.filters.get(kind) {
            Some(creator) => creator.create_filter(config),
            None => Err(Box::new(StringError(format!("No creator registered for filter kind \"{}\"", kind))))
        }
    }
}

/// Errors encountered when parsing a log4rs TOML config.
#[derive(Debug)]
pub struct ParseErrors {
    errors: Vec<String>,
}

impl fmt::Display for ParseErrors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for ParseErrors {
    fn description(&self) -> &str {
        "Errors encountered when parsing a log4rs TOML config"
    }
}

/// An error returned when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub enum Error {
    /// An error instantiating an appender.
    AppenderCreation(String, Box<error::Error>),
    /// An error instantiating a filter.
    FilterCreation(String, Box<error::Error>),
    /// An error when creating the log4rs `Config`.
    Config(config::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::AppenderCreation(ref appender, ref err) => {
                write!(fmt, "Error creating appender `{}`: {}", appender, err)
            }
            Error::FilterCreation(ref appender, ref err) => {
                write!(fmt, "Error creating filter for appender `{}`: {}", appender, err)
            }
            Error::Config(ref err) => write!(fmt, "Error creating config: {}", err),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "An error encountered when deserializing a TOML configuration into a log4rs `Config`"
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::AppenderCreation(_, ref err) => Some(&**err),
            Error::FilterCreation(_, ref err) => Some(&**err),
            Error::Config(ref err) => Some(err),
        }
    }
}

/// Errors encountered when deserializing a TOML configuration into a log4rs `Config`.
#[derive(Debug)]
pub struct Errors {
    errors: Vec<Error>,
}

impl Errors {
    /// Returns the list of errors encountered.
    pub fn errors(&self) -> &[Error] {
        &self.errors
    }
}

impl fmt::Display for Errors {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.errors {
            try!(writeln!(fmt, "{}", error));
        }
        Ok(())
    }
}

impl error::Error for Errors {
    fn description(&self) -> &str {
        "Errors encountered when deserializing a TOML configuration into a log4rs `Config`"
    }
}

/// A deserialized TOML log4rs configuration.
pub struct Config {
    refresh_rate: Option<Duration>,
    config: config::Config,
}

impl Config {
    /// Creates a log4rs `Config` from the specified TOML config string and `Creator`.
    pub fn parse(config: &str, creator: &Creator)
                 -> Result<(Config, Result<(), Errors>), ParseErrors> {
        let mut errors = vec![];

        let config = match raw::parse(config) {
            Ok(config) => config,
            Err(errors) => return Err(ParseErrors { errors: errors }),
        };

        let raw::Config {
            refresh_rate,
            root: raw_root,
            appenders: raw_appenders,
            loggers: raw_loggers,
        } = config;

        let root = match raw_root {
            Some(raw_root) => {
                let mut root = config::Root::builder(raw_root.level);
                if let Some(appenders) = raw_root.appenders {
                    root = root.appenders(appenders);
                }
                root.build()
            }
            None => config::Root::builder(LogLevelFilter::Debug).build(),
        };

        let mut config = config::Config::builder(root);

        for (name, raw::Appender { kind, config: raw_config, filters }) in raw_appenders {
            match creator.create_appender(&kind, raw_config) {
                Ok(appender_obj) => {
                    let mut builder = config::Appender::builder(name.clone(), appender_obj);
                    for raw::Filter { kind, config } in filters.unwrap_or(vec![]) {
                        match creator.create_filter(&kind, config) {
                            Ok(filter) => builder = builder.filter(filter),
                            Err(err) => errors.push(Error::FilterCreation(name.clone(), err)),
                        }
                    }
                    config = config.appender(builder.build());
                }
                Err(err) => errors.push(Error::AppenderCreation(name, err)),
            }
        }

        for logger in raw_loggers {
            let raw::Logger { name, level, appenders, additive } = logger;
            let mut logger = config::Logger::builder(name, level);
            if let Some(appenders) = appenders {
                logger = logger.appenders(appenders);
            }
            if let Some(additive) = additive {
                logger = logger.additive(additive);
            }
            config = config.logger(logger.build());
        }

        let (config, config_errors) = config.build_lossy();
        if let Err(config_errors) = config_errors {
            for error in config_errors.unpack() {
                errors.push(Error::Config(error));
            }
        }

        let config = Config {
            refresh_rate: refresh_rate,
            config: config
        };

        let errors = if errors.is_empty() {
            Ok(())
        } else {
            Err(Errors {
                errors: errors
            })
        };

        Ok((config, errors))
    }

    /// Returns the requested refresh rate.
    pub fn refresh_rate(&self) -> Option<Duration> {
        self.refresh_rate
    }

    /// Returns the log4rs `Config`.
    pub fn config(&self) -> &config::Config {
        &self.config
    }
}

impl PrivateTomlConfigExt for Config {
    fn unpack(self) -> (Option<Duration>, config::Config) {
        let Config { refresh_rate, config } = self;
        (refresh_rate, config)
    }
}

#[derive(Debug)]
struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(&self.0)
    }
}

impl error::Error for StringError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl error::FromError<String> for StringError {
    fn from_error(s: String) -> StringError {
        StringError(s)
    }
}

fn ensure_empty(config: &toml_parser::Table) -> Result<(), Box<error::Error>> {
    let remaining_keys: Vec<_> = config.keys().collect();
    if remaining_keys.is_empty() {
        Ok(())
    } else {
        Err(Box::new(StringError(format!("Unknown keys: {}", remaining_keys.connect(", ")))))
    }
}

/// An appender creator for the `FileAppender`.
///
/// The `path` key is required, and specifies the path to the log file. The
/// `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output. The `append` key is optional and specifies whether the
/// output file should be truncated or appended to.
pub struct FileAppenderCreator;

impl CreateAppender for FileAppenderCreator {
    fn create_appender(&self, mut config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let path = match config.remove("path") {
            Some(Value::String(path)) => path,
            Some(_) => return Err(Box::new(StringError("`path` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`path` is required".to_string()))),
        };

        let mut appender = FileAppender::builder(&path);
        match config.remove("pattern") {
            Some(Value::String(pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(&pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        match config.remove("append") {
            Some(Value::Boolean(append)) => appender = appender.append(append),
            None => {}
            Some(_) => return Err(Box::new(StringError("`append` must be a bool".to_string()))),
        }

        try!(ensure_empty(&config));
        match appender.build() {
            Ok(appender) => Ok(Box::new(appender)),
            Err(err) => Err(Box::new(err))
        }
    }
}

/// An appender creator for the `ConsoleAppender`.
///
/// The `pattern` key is optional and specifies a `PatternLayout` pattern to be
/// used for output.
pub struct ConsoleAppenderCreator;

impl CreateAppender for ConsoleAppenderCreator {
    fn create_appender(&self, mut config: toml_parser::Table)
                       -> Result<Box<Append>, Box<error::Error>> {
        let mut appender = ConsoleAppender::builder();
        match config.remove("pattern") {
            Some(Value::String(pattern)) => {
                appender = appender.pattern(try!(PatternLayout::new(&pattern)));
            }
            Some(_) => return Err(Box::new(StringError("`pattern` must be a string".to_string()))),
            None => {}
        }

        try!(ensure_empty(&config));
        Ok(Box::new(appender.build()))
    }
}

/// A filter creator for the `ThresholdFilter`.
///
/// The `level` key is required and specifies the threshold for the filter.
pub struct ThresholdFilterCreator;

impl CreateFilter for ThresholdFilterCreator {
    fn create_filter(&self, mut config: toml_parser::Table)
                     -> Result<Box<Filter>, Box<error::Error>> {
        let level = match config.remove("level") {
            Some(Value::String(level)) => level,
            Some(_) => return Err(Box::new(StringError("`level` must be a string".to_string()))),
            None => return Err(Box::new(StringError("`level` must be provided".to_string()))),
        };

        let level = match level.parse() {
            Ok(level) => level,
            Err(_) => return Err(Box::new(StringError(format!("Invalid `level` \"{}\"", level)))),
        };

        try!(ensure_empty(&config));
        Ok(Box::new(ThresholdFilter::new(level)))
    }
}
<|endoftext|>


<commit_before>//! Unix specific definitions
use std;
use std::io::{self, Chars, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::poll;
use nix::sys::signal;
use nix::sys::termios;

use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::{RawMode, RawReader, Term};

const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            for iter in &UNSUPPORTED_TERM {
                if (*iter).eq_ignore_ascii_case(&term) {
                    return true;
                }
            }
            false
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

pub type Mode = termios::Termios;

impl RawMode for Mode {
    /// Disable RAW mode for the terminal.
    fn disable_raw_mode(&self) -> Result<()> {
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, self));
        Ok(())
    }
}

// Rust std::io::Stdin is buffered with no way to know if bytes are available.
// So we use low-level stuff instead...
struct StdinRaw {}

impl Read for StdinRaw {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let res = unsafe {
            libc::read(STDIN_FILENO,
                       buf.as_mut_ptr() as *mut libc::c_void,
                       buf.len() as libc::size_t)
        };
        if res == -1 {
            Err(io::Error::last_os_error())
        } else {
            Ok(res as usize)
        }
    }
}

/// Console input reader
pub struct PosixRawReader {
    chars: Chars<StdinRaw>,
}

impl PosixRawReader {
    fn new() -> Result<PosixRawReader> {
        let stdin = StdinRaw {};
        Ok(PosixRawReader { chars: stdin.chars() })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    fn next_key(&mut self, timeout_ms: i32) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc {
            let mut fds =
                [poll::PollFd::new(STDIN_FILENO, poll::POLLIN, poll::EventFlags::empty())];
            match poll::poll(&mut fds, timeout_ms) {
                Ok(n) if n == 0 => {
                    // single escape
                }
                Ok(_) => {
                    // escape sequence
                    key = try!(self.escape_sequence())
                }
                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(e) => return Err(e.into()),
            }
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone,Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl Term for PosixTerminal {
    type Reader = PosixRawReader;
    type Mode = Mode;

    fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Try to get the number of columns in the current terminal,
    /// or assume 80 if it fails.
    fn get_columns(&self) -> usize {
        let (cols, _) = get_win_size();
        cols
    }

    /// Try to get the number of rows in the current terminal,
    /// or assume 24 if it fails.
    fn get_rows(&self) -> usize {
        let (_, rows) = get_win_size();
        rows
    }

    fn enable_raw_mode(&self) -> Result<Mode> {
        use nix::errno::Errno::ENOTTY;
        use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP,
                                IXON, /* OPOST, */ VMIN, VTIME};
        if !self.stdin_isatty {
            try!(Err(nix::Error::from_errno(ENOTTY)));
        }
        let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
        let mut raw = original_mode;
        // disable BREAK interrupt, CR to NL conversion on input,
        // input parity check, strip high bit (bit 8), output flow control
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        // we don't want raw output, it turns newlines into straight linefeeds
        // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
        raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
        // disable echoing, canonical mode, extended input processing and signals
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1; // One character-at-a-time input
        raw.c_cc[VTIME] = 0; // with blocking read
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &raw));
        Ok(original_mode)
    }

    /// Create a RAW reader
    fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new()
    }

    /// Check if a SIGWINCH signal has been received
    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        try!(w.write_all(b"\x1b[H\x1b[2J"));
        try!(w.flush());
        Ok(())
    }
}

#[cfg(unix)]
pub fn suspend() -> Result<()> {
    // For macos:
    try!(signal::kill(nix::unistd::getppid(), signal::SIGTSTP));
    try!(signal::kill(nix::unistd::getpid(), signal::SIGTSTP));
    Ok(())
}

#[cfg(all(unix,test))]
mod test {
    #[test]
    fn test_unsupported_term() {
        ::std::env::set_var("TERM", "xterm");
        assert_eq!(false, super::is_unsupported_term());

        ::std::env::set_var("TERM", "dumb");
        assert_eq!(true, super::is_unsupported_term());
    }
}
<commit_msg>Retry if read was interrupted by a signal<commit_after>//! Unix specific definitions
use std;
use std::io::{self, Chars, Read, Write};
use std::sync;
use std::sync::atomic;
use libc;
use nix;
use nix::poll;
use nix::sys::signal;
use nix::sys::termios;

use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::{RawMode, RawReader, Term};

const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

fn get_win_size() -> (usize, usize) {
    use std::mem::zeroed;
    use libc::c_ushort;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
            _ => (80, 24),
        }
    }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            for iter in &UNSUPPORTED_TERM {
                if (*iter).eq_ignore_ascii_case(&term) {
                    return true;
                }
            }
            false
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

pub type Mode = termios::Termios;

impl RawMode for Mode {
    /// Disable RAW mode for the terminal.
    fn disable_raw_mode(&self) -> Result<()> {
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, self));
        Ok(())
    }
}

// Rust std::io::Stdin is buffered with no way to know if bytes are available.
// So we use low-level stuff instead...
struct StdinRaw {}

impl Read for StdinRaw {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            let res = unsafe {
                libc::read(STDIN_FILENO,
                           buf.as_mut_ptr() as *mut libc::c_void,
                           buf.len() as libc::size_t)
            };
            if res == -1 {
                let error = io::Error::last_os_error();
                if error.kind() != io::ErrorKind::Interrupted {
                    return Err(error);
                }
            } else {
                return Ok(res as usize);
            }
        }
    }
}

/// Console input reader
pub struct PosixRawReader {
    chars: Chars<StdinRaw>,
}

impl PosixRawReader {
    fn new() -> Result<PosixRawReader> {
        let stdin = StdinRaw {};
        Ok(PosixRawReader { chars: stdin.chars() })
    }

    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    match seq2 {
                        // '1' => Ok(KeyPress::Home),
                        '3' => Ok(KeyPress::Delete),
                        // '4' => Ok(KeyPress::End),
                        '5' => Ok(KeyPress::PageUp),
                        '6' => Ok(KeyPress::PageDown),
                        '7' => Ok(KeyPress::Home),
                        '8' => Ok(KeyPress::End),
                        _ => Ok(KeyPress::UnknownEscSeq),
                    }
                } else {
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                match seq2 {
                    'A' => Ok(KeyPress::Up), // ANSI
                    'B' => Ok(KeyPress::Down),
                    'C' => Ok(KeyPress::Right),
                    'D' => Ok(KeyPress::Left),
                    'F' => Ok(KeyPress::End),
                    'H' => Ok(KeyPress::Home),
                    _ => Ok(KeyPress::UnknownEscSeq),
                }
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            match seq2 {
                'A' => Ok(KeyPress::Up),
                'B' => Ok(KeyPress::Down),
                'C' => Ok(KeyPress::Right),
                'D' => Ok(KeyPress::Left),
                'F' => Ok(KeyPress::End),
                'H' => Ok(KeyPress::Home),
                _ => Ok(KeyPress::UnknownEscSeq),
            }
        } else {
            // TODO ESC-N (n): search history forward not interactively
            // TODO ESC-P (p): search history backward not interactively
            // TODO ESC-R (r): Undo all changes made to this line.
            match seq1 {
                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
                '<' => Ok(KeyPress::Meta('<')),
                '>' => Ok(KeyPress::Meta('>')),
                'b' | 'B' => Ok(KeyPress::Meta('B')),
                'c' | 'C' => Ok(KeyPress::Meta('C')),
                'd' | 'D' => Ok(KeyPress::Meta('D')),
                'f' | 'F' => Ok(KeyPress::Meta('F')),
                'l' | 'L' => Ok(KeyPress::Meta('L')),
                't' | 'T' => Ok(KeyPress::Meta('T')),
                'u' | 'U' => Ok(KeyPress::Meta('U')),
                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
                _ => {
                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
                    Ok(KeyPress::UnknownEscSeq)
                }
            }
        }
    }
}

impl RawReader for PosixRawReader {
    fn next_key(&mut self, timeout_ms: i32) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc {
            let mut fds =
                [poll::PollFd::new(STDIN_FILENO, poll::POLLIN, poll::EventFlags::empty())];
            match poll::poll(&mut fds, timeout_ms) {
                Ok(n) if n == 0 => {
                    // single escape
                }
                Ok(_) => {
                    // escape sequence
                    key = try!(self.escape_sequence())
                }
                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(e) => return Err(e.into()),
            }
        }
        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(c) => Ok(try!(c)),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn install_sigwinch_handler() {
    SIGWINCH_ONCE.call_once(|| unsafe {
        let sigwinch = signal::SigAction::new(signal::SigHandler::Handler(sigwinch_handler),
                                              signal::SaFlags::empty(),
                                              signal::SigSet::empty());
        let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
    });
}

extern "C" fn sigwinch_handler(_: libc::c_int) {
    SIGWINCH.store(true, atomic::Ordering::SeqCst);
}

pub type Terminal = PosixTerminal;

#[derive(Clone,Debug)]
pub struct PosixTerminal {
    unsupported: bool,
    stdin_isatty: bool,
}

impl Term for PosixTerminal {
    type Reader = PosixRawReader;
    type Mode = Mode;

    fn new() -> PosixTerminal {
        let term = PosixTerminal {
            unsupported: is_unsupported_term(),
            stdin_isatty: is_a_tty(STDIN_FILENO),
        };
        if !term.unsupported && term.stdin_isatty && is_a_tty(STDOUT_FILENO) {
            install_sigwinch_handler();
        }
        term
    }

    // Init checks:

    /// Check if current terminal can provide a rich line-editing user interface.
    fn is_unsupported(&self) -> bool {
        self.unsupported
    }

    /// check if stdin is connected to a terminal.
    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    // Interactive loop:

    /// Try to get the number of columns in the current terminal,
    /// or assume 80 if it fails.
    fn get_columns(&self) -> usize {
        let (cols, _) = get_win_size();
        cols
    }

    /// Try to get the number of rows in the current terminal,
    /// or assume 24 if it fails.
    fn get_rows(&self) -> usize {
        let (_, rows) = get_win_size();
        rows
    }

    fn enable_raw_mode(&self) -> Result<Mode> {
        use nix::errno::Errno::ENOTTY;
        use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP,
                                IXON, /* OPOST, */ VMIN, VTIME};
        if !self.stdin_isatty {
            try!(Err(nix::Error::from_errno(ENOTTY)));
        }
        let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
        let mut raw = original_mode;
        // disable BREAK interrupt, CR to NL conversion on input,
        // input parity check, strip high bit (bit 8), output flow control
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        // we don't want raw output, it turns newlines into straight linefeeds
        // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
        raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
        // disable echoing, canonical mode, extended input processing and signals
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1; // One character-at-a-time input
        raw.c_cc[VTIME] = 0; // with blocking read
        try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &raw));
        Ok(original_mode)
    }

    /// Create a RAW reader
    fn create_reader(&self) -> Result<PosixRawReader> {
        PosixRawReader::new()
    }

    /// Check if a SIGWINCH signal has been received
    fn sigwinch(&self) -> bool {
        SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        try!(w.write_all(b"\x1b[H\x1b[2J"));
        try!(w.flush());
        Ok(())
    }
}

#[cfg(unix)]
pub fn suspend() -> Result<()> {
    // For macos:
    try!(signal::kill(nix::unistd::getppid(), signal::SIGTSTP));
    try!(signal::kill(nix::unistd::getpid(), signal::SIGTSTP));
    Ok(())
}

#[cfg(all(unix,test))]
mod test {
    #[test]
    fn test_unsupported_term() {
        ::std::env::set_var("TERM", "xterm");
        assert_eq!(false, super::is_unsupported_term());

        ::std::env::set_var("TERM", "dumb");
        assert_eq!(true, super::is_unsupported_term());
    }
}
<|endoftext|>


<commit_before>extern crate nix;
extern crate libc;

use std;
use nix::sys::termios;
use nix::errno::Errno;
use super::Terminal;
use super::StandardStream;
use ::Result;
use ::error;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
pub fn get_columns() -> usize {
    use std::mem::zeroed;
    use libc::c_ushort;
    use libc;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(libc::STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => size.ws_col as usize, // TODO getCursorPosition
            _ => 80,
        }
    }
}

/// Get UnixTerminal struct
pub fn get_terminal() -> UnixTerminal {
    UnixTerminal{ original_termios: None }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
pub fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = (*iter).eq_ignore_ascii_case(&term)
            }
            unsupported
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
pub fn is_a_tty(stream: StandardStream) -> bool {
    extern crate libc;

    let fd = match stream {
            StandardStream::StdIn => libc::STDIN_FILENO,
            StandardStream::StdOut => libc::STDOUT_FILENO,
        };

    unsafe { libc::isatty(fd) != 0 }
}

/// Structure that will contain the original termios before enabling RAW mode
pub struct UnixTerminal {
    original_termios: Option<termios::Termios>
}

impl Terminal for UnixTerminal {
    /// Enable raw mode for the TERM
    fn enable_raw_mode(&mut self) -> Result<()> {
        use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                                OPOST, VMIN, VTIME};
        if !is_a_tty(StandardStream::StdIn) {
            return Err(error::ReadlineError::from_errno(Errno::ENOTTY));
        }
        let original_termios = try!(termios::tcgetattr(libc::STDIN_FILENO));
        let mut raw = original_termios;
        // disable BREAK interrupt, CR to NL conversion on input,
        // input parity check, strip high bit (bit 8), output flow control
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
        raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
        // disable echoing, canonical mode, extended input processing and signals
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1; // One character-at-a-time input
        raw.c_cc[VTIME] = 0; // with blocking read
        try!(termios::tcsetattr(libc::STDIN_FILENO, termios::TCSAFLUSH, &raw));

        // Set the original terminal to the struct field
        self.original_termios = Some(original_termios);
        Ok(())
    }

    /// Disable Raw mode for the term
    fn disable_raw_mode(&self) -> Result<()> {
        try!(termios::tcsetattr(libc::STDIN_FILENO,
                                termios::TCSAFLUSH,
                                &self.original_termios.expect("RAW MODE was not enabled previously")));
        Ok(())
    }
}

/// Ensure that RAW mode is disabled even in the case of a panic!
#[allow(unused_must_use)]
impl Drop for UnixTerminal {
    fn drop(&mut self) {
        self.disable_raw_mode();
    }
}
<commit_msg>Porting 3e3db21 to windows_support branch<commit_after>extern crate nix;
extern crate libc;

use std;
use nix::sys::termios;
use nix::errno::Errno;
use super::Terminal;
use super::StandardStream;
use ::Result;
use ::error;

/// Unsupported Terminals that don't support RAW mode
static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
const TIOCGWINSZ: libc::c_ulong = 0x40087468;

#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "android"))]
const TIOCGWINSZ: libc::c_ulong = 0x5413;

#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;

/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
pub fn get_columns() -> usize {
    use std::mem::zeroed;
    use libc::c_ushort;
    use libc;

    unsafe {
        #[repr(C)]
        struct winsize {
            ws_row: c_ushort,
            ws_col: c_ushort,
            ws_xpixel: c_ushort,
            ws_ypixel: c_ushort,
        }

        let mut size: winsize = zeroed();
        match libc::ioctl(libc::STDOUT_FILENO, TIOCGWINSZ, &mut size) {
            0 => size.ws_col as usize, // TODO getCursorPosition
            _ => 80,
        }
    }
}

/// Get UnixTerminal struct
pub fn get_terminal() -> UnixTerminal {
    UnixTerminal{ original_termios: None }
}

/// Check TERM environment variable to see if current term is in our
/// unsupported list
pub fn is_unsupported_term() -> bool {
    use std::ascii::AsciiExt;
    match std::env::var("TERM") {
        Ok(term) => {
            let mut unsupported = false;
            for iter in &UNSUPPORTED_TERM {
                unsupported = (*iter).eq_ignore_ascii_case(&term)
            }
            unsupported
        }
        Err(_) => false,
    }
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
pub fn is_a_tty(stream: StandardStream) -> bool {
    extern crate libc;

    let fd = match stream {
            StandardStream::StdIn => libc::STDIN_FILENO,
            StandardStream::StdOut => libc::STDOUT_FILENO,
        };

    unsafe { libc::isatty(fd) != 0 }
}

/// Structure that will contain the original termios before enabling RAW mode
pub struct UnixTerminal {
    original_termios: Option<termios::Termios>
}

impl Terminal for UnixTerminal {
    /// Enable raw mode for the TERM
    fn enable_raw_mode(&mut self) -> Result<()> {
        use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
                                /*OPOST, */VMIN, VTIME};
        if !is_a_tty(StandardStream::StdIn) {
            return Err(error::ReadlineError::from_errno(Errno::ENOTTY));
        }
        let original_termios = try!(termios::tcgetattr(libc::STDIN_FILENO));
        let mut raw = original_termios;
        // disable BREAK interrupt, CR to NL conversion on input,
        // input parity check, strip high bit (bit 8), output flow control
        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        // we don't want raw output, it turns newlines into straight linefeeds
        //raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
        raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
        // disable echoing, canonical mode, extended input processing and signals
        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
        raw.c_cc[VMIN] = 1; // One character-at-a-time input
        raw.c_cc[VTIME] = 0; // with blocking read
        try!(termios::tcsetattr(libc::STDIN_FILENO, termios::TCSAFLUSH, &raw));

        // Set the original terminal to the struct field
        self.original_termios = Some(original_termios);
        Ok(())
    }

    /// Disable Raw mode for the term
    fn disable_raw_mode(&self) -> Result<()> {
        try!(termios::tcsetattr(libc::STDIN_FILENO,
                                termios::TCSAFLUSH,
                                &self.original_termios.expect("RAW MODE was not enabled previously")));
        Ok(())
    }
}

/// Ensure that RAW mode is disabled even in the case of a panic!
#[allow(unused_must_use)]
impl Drop for UnixTerminal {
    fn drop(&mut self) {
        self.disable_raw_mode();
    }
}
<|endoftext|>


<commit_before>use tokio::net::TcpListener;

use std::io;

async fn process_socket<T>(socket: T) {}

#[tokio::main]
async fn main() -> io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8080").await?;

    loop {
        let (socket, _) = listener.accept().await?;
        process_socket(socket).await;
    }
}
<commit_msg>Add function<commit_after>use tokio::net::TcpListener;

use std::io;

async fn process_socket<T>(socket: T, port: &str) {
    TcpStream::connect();
}

#[tokio::main]
async fn main() -> io::Result<()> {
    let output_port: &str = "";
    let listener = TcpListener::bind("127.0.0.1:8080").await?;

    loop {
        let (socket, _) = listener.accept().await?;
        process_socket(socket, output_port).await;
    }
}
<|endoftext|>


<commit_before>extern crate rand;
use rand::{thread_rng, Rng};
use rand::distributions::{IndependentSample, Range};
use std::env::args as cmd_args;

#[inline(always)]
fn usage() {
    println!("Usage:");
    println!("    gen_random [number of digits]");
}

fn gen_random_number_string(number: usize) -> String {
    let num_range = Range::new(0, 9);
    let mut rng = rand::thread_rng();
    let mut final_string = String::with_capacity(number);
    for _ in 0..number {
        final_string.push_str(format!("{}", num_range.ind_sample(&mut rng)).as_ref());
    }
    final_string
}

fn gen_random_number(number: usize) -> usize {
    thread_rng().gen_range((10 as usize).pow((number-1) as u32), (10 as usize).pow(number as u32) - 1)
}

fn main() {
    if let Some(arg1) = cmd_args().skip(1).next() {
        if let Ok(arg1_number) = arg1.parse::<usize>() {
            if arg1_number == 0 { return; }

            println!("Got number: {}", gen_random_number_string(arg1_number));
        }
        else {
            println!("Cannon parse >{}< into positive number", &arg1);
        }
    }
    else { usage() }
}
<commit_msg>Rust: gen_rand_nubmer remove unused code<commit_after>extern crate rand;
use rand::{thread_rng};
use rand::distributions::{IndependentSample, Range};
use std::env::args as cmd_args;

#[inline(always)]
fn usage() {
    println!("Usage:");
    println!("    gen_random [number of digits]");
}

fn gen_random_number_string(number: usize) -> String {
    let num_range = Range::new(0, 9);
    let mut rng = rand::thread_rng();
    let mut final_string = String::with_capacity(number);
    for _ in 0..number {
        final_string.push_str(format!("{}", num_range.ind_sample(&mut rng)).as_ref());
    }
    final_string
}

fn main() {
    if let Some(arg1) = cmd_args().skip(1).next() {
        if let Ok(arg1_number) = arg1.parse::<usize>() {
            if arg1_number == 0 { return; }

            println!("Got number: {}", gen_random_number_string(arg1_number));
        }
        else {
            println!("Cannon parse >{}< into positive number", &arg1);
        }
    }
    else { usage() }
}
<|endoftext|>


<commit_before>extern crate byteorder;

use std::cmp::{Ord, Ordering};

use std::io::Cursor;
use self::byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use super::schema::{Schema, Type};

#[derive(Debug, Clone, PartialEq)]
pub struct Value {
    data: Vec<u8>
}

impl Value {
    fn to_int(&self) -> i64 {
        let mut cur = Cursor::new(self.data.clone());
        cur.read_i64::<LittleEndian>().unwrap()
    }
    fn to_string(&self) -> String {
        String::from_utf8(self.data.clone()).unwrap()
    }
}

impl From<i64> for Value {
    fn from(val: i64) -> Value {
        let mut buffer = Vec::new();
        buffer.write_i64::<LittleEndian>(val).unwrap();
        Value { data: buffer }
    }
}

impl From<String> for Value {
    fn from(val: String) -> Value {
        Value{data:val.into_bytes()}
    }
}

impl<'a> From<&'a str> for Value {
    fn from(val: &'a str) -> Value {
        let tmp = val.as_bytes();
        let mut v : Vec<u8> = Vec::new();
        v.extend_from_slice(&tmp);
        Value{data:v}
    }
}

struct ValueComparator {
    val: Value,
    dtype: Type,
}

impl ValueComparator {
    fn new(val: Value, dtype: Type) -> ValueComparator {
        ValueComparator{val:val, dtype:dtype}
    }
}

impl Ord for ValueComparator {
    fn cmp(&self, other: &ValueComparator) -> Ordering {
        Ordering::Equal
    }
}

impl PartialEq for ValueComparator {
    fn eq(&self, other: &ValueComparator) -> bool {
        true
    }
}

impl Eq for ValueComparator {}

impl PartialOrd for ValueComparator {
    fn partial_cmp(&self, other: &ValueComparator) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(test)]
mod tests {
    use super::Value;
    #[test]
    fn ints_should_compare_equal() {
        let mut x = Value::from(1);
        let mut y = Value::from(1);
        assert_eq!(x, y);

        y = Value::from(2);
        assert!(x != y);
    }

    #[test]
    fn int_converts_back_ok() {
        let x = Value::from(1);
        let y = x.to_int();
        assert_eq!(1, y);
    }

    #[test]
    fn string_equality() {
        let x = Value::from("test");
        let y = Value::from("test");
        assert_eq!(x, y);
        let y = Value::from("test2");
        assert!(x != y);
    }

    #[test]
    fn string_conversions() {
        let tmp = Value::from("this is a test");
        let x = tmp.to_string();
        assert_eq!(x, "this is a test");
    }
}<commit_msg>equality may be working holy shit<commit_after>extern crate byteorder;

use std::cmp::{Ord, Ordering};

use std::io::Cursor;
use self::byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use super::schema::{Schema, Type};

#[derive(Debug, Clone, PartialEq)]
pub struct Value {
    data: Vec<u8>
}

impl Value {
    fn to_int(&self) -> i64 {
        let mut cur = Cursor::new(self.data.clone());
        cur.read_i64::<LittleEndian>().unwrap()
    }
    fn to_string(&self) -> String {
        String::from_utf8(self.data.clone()).unwrap()
    }
}

impl From<i64> for Value {
    fn from(val: i64) -> Value {
        let mut buffer = Vec::new();
        buffer.write_i64::<LittleEndian>(val).unwrap();
        Value { data: buffer }
    }
}

impl From<String> for Value {
    fn from(val: String) -> Value {
        Value{data:val.into_bytes()}
    }
}

impl<'a> From<&'a str> for Value {
    fn from(val: &'a str) -> Value {
        let tmp = val.as_bytes();
        let mut v : Vec<u8> = Vec::new();
        v.extend_from_slice(&tmp);
        Value{data:v}
    }
}

struct ValueComparator {
    val: Value,
    dtype: Type,
}

impl ValueComparator {
    fn new(val: Value, dtype: Type) -> ValueComparator {
        ValueComparator{val:val, dtype:dtype}
    }
}

impl Ord for ValueComparator {
    fn cmp(&self, other: &ValueComparator) -> Ordering {
        Ordering::Equal
    }
}

impl PartialEq for ValueComparator {
    fn eq(&self, other: &ValueComparator) -> bool {
        match (&self.dtype, &other.dtype) {
            (&Type::Int, &Type::Int) => self.val.to_int() == other.val.to_int(),
            _ => false
        }
    }
}

impl Eq for ValueComparator {}

impl PartialOrd for ValueComparator {
    fn partial_cmp(&self, other: &ValueComparator) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(test)]
mod tests {
    use super::Value;
    #[test]
    fn ints_should_compare_equal() {
        let mut x = Value::from(1);
        let mut y = Value::from(1);
        assert_eq!(x, y);

        y = Value::from(2);
        assert!(x != y);
    }

    #[test]
    fn int_converts_back_ok() {
        let x = Value::from(1);
        let y = x.to_int();
        assert_eq!(1, y);
    }

    #[test]
    fn string_equality() {
        let x = Value::from("test");
        let y = Value::from("test");
        assert_eq!(x, y);
        let y = Value::from("test2");
        assert!(x != y);
    }

    #[test]
    fn string_conversions() {
        let tmp = Value::from("this is a test");
        let x = tmp.to_string();
        assert_eq!(x, "this is a test");
    }
}<|endoftext|>