<commit_before>//! A simple `io::stdout` and `io::stderr` writing `Logger` implementation from the
//! `log` crate, using the `ansi_term` crate for colors and configured at runtime,
//! or at compile time with simple function calls. Designed for simple CLIs.
//!
//! This library only comes with 3 public ways to initialize the global logger.
//! Ensure you call one of these exactly once early in your rust program as shown
//! in one of the examples below.
//!
//! ## Example
//! The standard example with `clap` as the arg parser.
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate clap;
//! extern crate loggerv;
//!
//! use clap::{Arg, App};
//!
//! fn main() {
//!     let args = App::new("app")
//!                    .arg(Arg::with_name("v")
//!                             .short("v")
//!                             .multiple(true)
//!                             .help("Sets the level of verbosity"))
//!                    .get_matches();
//!
//!     loggerv::init_with_verbosity(args.occurrences_of("v")).unwrap();
//!
//!     error!("this is always printed");
//!     warn!("this too, and it's printed to stderr");
//!     info!("this is optional");  // for ./app -v or higher
//!     debug!("this is optional"); // for ./app -vv or higher
//!     trace!("this is optional"); // for ./app -vvv
//! }
//! ```
//!
//! But obviously use whatever argument parsing methods you prefer.
//!
//! ## Example
//! For a compile time switch, all you really need is `log` (for the macros)
//! and `loggerv` for how to print what's being sent to the macros.
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate loggerv;
//!
//! use log::LogLevel;
//!
//! fn main() {
//!     loggerv::init_with_level(LogLevel::Info).unwrap();
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//! }
//! ```
//!
//! # Example
//! If you don't really care at all you could just use the plain `init_quiet` function
//! to only show warnings and errors:
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate loggerv;
//!
//! fn main() {
//!     loggerv::init_quiet().unwrap();
//!     info!("hidden");
//!     error!("this is printed by default");
//! }
//! ```
//!
//!
//! See the documentation for the log crate for more information about its API.
//!

#[cfg(test)]
#[macro_use]
extern crate log;

#[cfg(not(test))]
extern crate log;

extern crate atty;
extern crate ansi_term;

use log::{Log, LogLevel, LogMetadata, LogRecord, SetLoggerError};
use std::io::{self, Write};
use ansi_term::Colour;

pub const DEFAULT_COLORS: bool = true;
pub const DEFAULT_SEPARATOR: &str = ": ";
pub const DEFAULT_LEVEL: LogLevel = LogLevel::Warn;
pub const DEFAULT_LINE_NUMBERS: bool = false;
pub const DEFAULT_MODULE_PATH: bool = true;

fn level_style(l: LogLevel) -> Colour {
    match l {
        LogLevel::Error => Colour::Fixed(9), // bright red
        LogLevel::Warn => Colour::Fixed(11), // bright yellow
        LogLevel::Info => Colour::Fixed(10), // bright green
        LogLevel::Debug => Colour::Fixed(7), // light grey
        LogLevel::Trace => Colour::Fixed(8), // grey
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Logger {
    colors: bool,
    line_numbers: bool,
    level: LogLevel,
    module_path: bool,
    separator: String,
}

impl Logger {
    /// Creates a new instance of the verbosity-based logger.
    ///
    /// The default level is WARN. Color is enabled if the parent application or library is running
    /// from a terminal, i.e. running a tty. The default separator is the ": " string. The default
    /// output format is `LEVEL [module path]: message`.
    pub fn new() -> Logger {
        Logger {
            colors: DEFAULT_COLORS && atty::is(atty::Stream::Stdout) && atty::is(atty::Stream::Stderr),
            line_numbers: DEFAULT_LINE_NUMBERS,
            level: DEFAULT_LEVEL,
            module_path: DEFAULT_MODULE_PATH,
            separator: String::from(DEFAULT_SEPARATOR),
        }
    }

    /// Sets the separator string.
    ///
    /// The separator is the string between the "tag" and the message that make up a log statement.
    /// The tag will be colorized if enabled, while the message will not. The default is `: `.
    pub fn separator(mut self, s: &str) -> Self {
        self.separator = String::from(s);
        self
    }

    /// Enables or disables colorizing the output.
    ///
    /// If the logger is _not_ used in a terminal, then
    /// the output is _not_ colorized regardless of the parameter value.
    pub fn colors(mut self, c: bool) -> Self {
        self.colors = c && atty::is(atty::Stream::Stdout) && atty::is(atty::Stream::Stderr);
        self
    }

    /// Enables or disables including line numbers in the "tag" portion of the log statement. The
    /// tag is the text to the left of the separator.
    pub fn line_numbers(mut self, l: bool) -> Self {
        self.line_numbers = l;
        self
    }

    /// Explicitly sets the log level instead through a verbosity.
    pub fn level(mut self, l: LogLevel) -> Self {
        self.level = l;
        self
    }

    /// Enables or disables including the module path in the "tag" portion of the log statement.
    /// The tag is the text to the left of the separator.
    pub fn module_path(mut self, m: bool) -> Self {
        self.module_path = m;
        self
    }

    /// Converts the verbosity to a log level.
    ///
    /// A verbosity of zero (0) is the default, which means ERROR and WARN log statements are
    /// printed to `stderr`. No other log statements are printed on any of the standard streams
    /// (`stdout` or `stderr`). As the verbosity is increased, the log level is increased and more
    /// log statements will be printed to `stdout`. A verbosity of 1 will print INFO log statements
    /// to `stdout` in addition to ERROR and WARN. A verbosity of 2 will print INFO and DEBUG log
    /// statements to `stdout`. A verbosity of 3 or higher will print INFO, DEBUG, and TRACE
    /// log statements to `stdout` with ERROR and WARN statements printed to `stderr`.
    pub fn verbosity(mut self, v: u64) -> Self {
        self.level = match v {
                0 => LogLevel::Warn,  // default
                1 => LogLevel::Info,  // -v
                2 => LogLevel::Debug, // -vv
                _ => LogLevel::Trace, // -vvv and above
        };
        self
    }

    /// Initializes the logger.
    ///
    /// This also consumes the logger and cannot no longer be modified after initialization.
    pub fn init(self) -> Result<(), SetLoggerError> {
        log::set_logger(|max_level| {
            max_level.set(self.level.to_log_level_filter());
            Box::new(self)
        })
    }
}

impl Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let level = record.level();
            let module_path = if self.module_path {
                format!(" [{}]", record.location().module_path())
            } else {
                String::new()
            };
            let line = if self.line_numbers {
                format!(" (line {})", record.location().line())
            } else {
                String::new()
            };
            let tag = if self.colors {
                level_style(level)
                    .paint(format!("{}{}{}", level, module_path, line))
                    .to_string()
            } else {
                format!("{}{}{}", level, module_path, line)
            };
            if level <= LogLevel::Warn {
                let _ = writeln!(&mut io::stderr(), "{}{}{}", tag, self.separator, record.args());
            } else {
                println!("{}{}{}", tag, self.separator, record.args());
            }
        }
    }
}

impl Default for Logger {
    fn default() -> Logger {
        Logger::new()
    }
}

/// Initialize loggerv with a maximal log level.
///
/// See the main loggerv documentation page for an example.
pub fn init_with_level(level: LogLevel) -> Result<(), SetLoggerError> {
    Logger::new().level(level).init()
}

/// Initialize loggerv with a verbosity level.
///
/// Intended to be used with an arg parser counting the amount of -v flags.
/// See the main loggerv documentation page for an example.
pub fn init_with_verbosity(verbosity: u64) -> Result<(), SetLoggerError> {
    Logger::new().verbosity(verbosity).init()
}

/// Initializes loggerv with only warnings and errors.
///
/// See the main loggerv documentation page for an example.
pub fn init_quiet() -> Result<(), SetLoggerError> {
    init_with_level(LogLevel::Warn)
}

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

    #[test]
    fn init_and_macros() {
        let l = init_with_verbosity(3);
        assert_eq!(l.is_ok(), true);
        error!("error log");
        warn!("warn log");
        info!("info log");
        debug!("debug log");
        trace!("trace log");
    }
}
<commit_msg>Add ability to change level colors<commit_after>//! A simple `io::stdout` and `io::stderr` writing `Logger` implementation from the
//! `log` crate, using the `ansi_term` crate for colors and configured at runtime,
//! or at compile time with simple function calls. Designed for simple CLIs.
//!
//! This library only comes with 3 public ways to initialize the global logger.
//! Ensure you call one of these exactly once early in your rust program as shown
//! in one of the examples below.
//!
//! ## Example
//! The standard example with `clap` as the arg parser.
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate clap;
//! extern crate loggerv;
//!
//! use clap::{Arg, App};
//!
//! fn main() {
//!     let args = App::new("app")
//!                    .arg(Arg::with_name("v")
//!                             .short("v")
//!                             .multiple(true)
//!                             .help("Sets the level of verbosity"))
//!                    .get_matches();
//!
//!     loggerv::init_with_verbosity(args.occurrences_of("v")).unwrap();
//!
//!     error!("this is always printed");
//!     warn!("this too, and it's printed to stderr");
//!     info!("this is optional");  // for ./app -v or higher
//!     debug!("this is optional"); // for ./app -vv or higher
//!     trace!("this is optional"); // for ./app -vvv
//! }
//! ```
//!
//! But obviously use whatever argument parsing methods you prefer.
//!
//! ## Example
//! For a compile time switch, all you really need is `log` (for the macros)
//! and `loggerv` for how to print what's being sent to the macros.
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate loggerv;
//!
//! use log::LogLevel;
//!
//! fn main() {
//!     loggerv::init_with_level(LogLevel::Info).unwrap();
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//! }
//! ```
//!
//! # Example
//! If you don't really care at all you could just use the plain `init_quiet` function
//! to only show warnings and errors:
//!
//! ```
//! #[macro_use] extern crate log;
//! extern crate loggerv;
//!
//! fn main() {
//!     loggerv::init_quiet().unwrap();
//!     info!("hidden");
//!     error!("this is printed by default");
//! }
//! ```
//!
//!
//! See the documentation for the log crate for more information about its API.
//!

#[cfg(test)]
#[macro_use]
extern crate log;

#[cfg(not(test))]
extern crate log;

extern crate atty;
extern crate ansi_term;

use log::{Log, LogLevel, LogMetadata, LogRecord, SetLoggerError};
use std::io::{self, Write};
use ansi_term::Colour;

pub const DEFAULT_COLORS: bool = true;
pub const DEFAULT_SEPARATOR: &str = ": ";
pub const DEFAULT_LEVEL: LogLevel = LogLevel::Warn;
pub const DEFAULT_LINE_NUMBERS: bool = false;
pub const DEFAULT_MODULE_PATH: bool = true;

#[derive(Debug, Clone, PartialEq)]
pub struct Logger {
    colors: bool,
    line_numbers: bool,
    level: LogLevel,
    module_path: bool,
    separator: String,
    error_color: Colour,
    warn_color: Colour,
    info_color: Colour,
    debug_color: Colour,
    trace_color: Colour,
}

impl Logger {
    /// Creates a new instance of the verbosity-based logger.
    ///
    /// The default level is WARN. Color is enabled if the parent application or library is running
    /// from a terminal, i.e. running a tty. The default separator is the ": " string. The default
    /// output format is `LEVEL [module path]: message`.
    pub fn new() -> Logger {
        Logger {
            colors: DEFAULT_COLORS && atty::is(atty::Stream::Stdout) && atty::is(atty::Stream::Stderr),
            line_numbers: DEFAULT_LINE_NUMBERS,
            level: DEFAULT_LEVEL,
            module_path: DEFAULT_MODULE_PATH,
            separator: String::from(DEFAULT_SEPARATOR),
            error_color: Colour::Fixed(9), // bright red
            warn_color: Colour::Fixed(11), // bright yellow
            info_color: Colour::Fixed(10), // bright green
            debug_color: Colour::Fixed(7), // light grey
            trace_color: Colour::Fixed(8), // grey
        }
    }

    /// Sets the output color for the ERROR level. The default is bright red.
    pub fn error_color(mut self, c: Colour) -> Self {
        self.error_color = c;
        self
    }

    /// Sets the output color for the WARN level. The default is bright yellow.
    pub fn warn_color(mut self, c: Colour) -> Self {
        self.warn_color = c;
        self
    }

    /// Sets the output color for the INFO level. The default is bright green.
    pub fn info_color(mut self, c: Colour) -> Self {
        self.info_color = c;
        self
    }

    /// Sets the output color for the DEBUG level. The default is light grey.
    pub fn debug_color(mut self, c: Colour) -> Self {
        self.debug_color = c;
        self
    }

    /// Sets the output color for the TRACE level. The default is grey.
    pub fn trace_color(mut self, c: Colour) -> Self {
        self.trace_color = c;
        self
    }

    /// Gets the color to use for the log statement's tag based on level.
    fn color(&self, l: &LogLevel) -> Colour {
        match *l {
            LogLevel::Error => self.error_color,
            LogLevel::Warn => self.warn_color,
            LogLevel::Info => self.info_color,
            LogLevel::Debug => self.debug_color,
            LogLevel::Trace => self.trace_color,
        }
    }

    /// Sets the separator string.
    ///
    /// The separator is the string between the "tag" and the message that make up a log statement.
    /// The tag will be colorized if enabled, while the message will not. The default is `: `.
    pub fn separator(mut self, s: &str) -> Self {
        self.separator = String::from(s);
        self
    }

    /// Enables or disables colorizing the output.
    ///
    /// If the logger is _not_ used in a terminal, then
    /// the output is _not_ colorized regardless of the parameter value.
    pub fn colors(mut self, c: bool) -> Self {
        self.colors = c && atty::is(atty::Stream::Stdout) && atty::is(atty::Stream::Stderr);
        self
    }

    /// Enables or disables including line numbers in the "tag" portion of the log statement. The
    /// tag is the text to the left of the separator.
    pub fn line_numbers(mut self, l: bool) -> Self {
        self.line_numbers = l;
        self
    }

    /// Explicitly sets the log level instead through a verbosity.
    pub fn level(mut self, l: LogLevel) -> Self {
        self.level = l;
        self
    }

    /// Enables or disables including the module path in the "tag" portion of the log statement.
    /// The tag is the text to the left of the separator.
    pub fn module_path(mut self, m: bool) -> Self {
        self.module_path = m;
        self
    }

    /// Converts the verbosity to a log level.
    ///
    /// A verbosity of zero (0) is the default, which means ERROR and WARN log statements are
    /// printed to `stderr`. No other log statements are printed on any of the standard streams
    /// (`stdout` or `stderr`). As the verbosity is increased, the log level is increased and more
    /// log statements will be printed to `stdout`. A verbosity of 1 will print INFO log statements
    /// to `stdout` in addition to ERROR and WARN. A verbosity of 2 will print INFO and DEBUG log
    /// statements to `stdout`. A verbosity of 3 or higher will print INFO, DEBUG, and TRACE
    /// log statements to `stdout` with ERROR and WARN statements printed to `stderr`.
    pub fn verbosity(mut self, v: u64) -> Self {
        self.level = match v {
                0 => LogLevel::Warn,  // default
                1 => LogLevel::Info,  // -v
                2 => LogLevel::Debug, // -vv
                _ => LogLevel::Trace, // -vvv and above
        };
        self
    }

    /// Initializes the logger.
    ///
    /// This also consumes the logger and cannot no longer be modified after initialization.
    pub fn init(self) -> Result<(), SetLoggerError> {
        log::set_logger(|max_level| {
            max_level.set(self.level.to_log_level_filter());
            Box::new(self)
        })
    }
}

impl Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let level = record.level();
            let module_path = if self.module_path {
                format!(" [{}]", record.location().module_path())
            } else {
                String::new()
            };
            let line = if self.line_numbers {
                format!(" (line {})", record.location().line())
            } else {
                String::new()
            };
            let tag = if self.colors {
                self.color(&level).paint(format!("{}{}{}", level, module_path, line)).to_string()
            } else {
                format!("{}{}{}", level, module_path, line)
            };
            if level <= LogLevel::Warn {
                let _ = writeln!(&mut io::stderr(), "{}{}{}", tag, self.separator, record.args());
            } else {
                println!("{}{}{}", tag, self.separator, record.args());
            }
        }
    }
}

impl Default for Logger {
    fn default() -> Logger {
        Logger::new()
    }
}

/// Initialize loggerv with a maximal log level.
///
/// See the main loggerv documentation page for an example.
pub fn init_with_level(level: LogLevel) -> Result<(), SetLoggerError> {
    Logger::new().level(level).init()
}

/// Initialize loggerv with a verbosity level.
///
/// Intended to be used with an arg parser counting the amount of -v flags.
/// See the main loggerv documentation page for an example.
pub fn init_with_verbosity(verbosity: u64) -> Result<(), SetLoggerError> {
    Logger::new().verbosity(verbosity).init()
}

/// Initializes loggerv with only warnings and errors.
///
/// See the main loggerv documentation page for an example.
pub fn init_quiet() -> Result<(), SetLoggerError> {
    init_with_level(LogLevel::Warn)
}

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

    #[test]
    fn init_and_macros() {
        let l = init_with_verbosity(3);
        assert_eq!(l.is_ok(), true);
        error!("error log");
        warn!("warn log");
        info!("info log");
        debug!("debug log");
        trace!("trace log");
    }
}
<|endoftext|>


<commit_before>extern crate num;
extern crate lodepng;
extern crate rustc_serialize;

use num::traits::{ Float, ToPrimitive };

//use std::path::Path;
use rustc_serialize::base64::{self, ToBase64};

pub fn parse_numbers (args : &[String]) -> Vec<f64> {
    args.iter().enumerate().map(|(i, x)| {
        x.parse::<f64>().ok().expect(&format!("Argument \"{}\" was not a number :(", args[i]))
    }).collect()
}

/// Find the minimum and maximum for a slice of Float values.
///
/// ```
/// use sparkline::*;
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], None, None);
/// assert_eq!(x, 0.0);
/// assert_eq!(y, 2.0);
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], Some(-1.0), None);
/// assert_eq!(x, -1.0);
/// assert_eq!(y, 2.0);
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], Some(1.0), None);
/// assert_eq!(x, 1.0);
/// assert_eq!(y, 2.0);
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], None, Some(1.5));
/// assert_eq!(x, 0.0);
/// assert_eq!(y, 1.5);
/// ```
pub fn min_max_for_data<T>(numbers: &[T], min_opt: Option<T>, max_opt: Option<T>) -> (f64, f64) where T: Float {
    let max = match max_opt {
        Some(m) => m,
        None => numbers.iter().fold(T::min_value(), |a, b| a.max(*b)),
    };
    let min = match min_opt {
        Some(m) => m,
        None => numbers.iter().fold(T::max_value(), |a, b| a.min(*b)),
    };
    (min.to_f64().unwrap(), max.to_f64().unwrap())
}

pub trait SparkTheme {
    fn start(&mut self, min : f64, max : f64);
    fn spark(&mut self, pos: usize, length: usize, num : f64) -> &str;
    fn end(&mut self) {}

    fn minmax(&self) -> (f64, f64);
    fn proportion(&self, num : f64) -> f64 {
        let (min, max) = self.minmax();

        (num - min) / (max - min)
    }
}

pub struct MappingTheme {
    pub sparks : Vec<String>,
    min : f64,
    max : f64,
}

impl SparkTheme for MappingTheme {
    fn start(&mut self, min : f64, max : f64) {
        self.min = min;
        self.max = max;
    }

    fn minmax(&self) -> (f64, f64) {
        (self.min, self.max)
    }

    fn spark(&mut self, _pos: usize, _length: usize, num : f64) -> &str {
        let increments = self.sparks.len() as f64;

        let mut proportion = (increments) * self.proportion(num);

        // If num == max, then proportion will be out of bounds, so drop one
        if proportion == increments {
            proportion = proportion - 1.0;
        }

        &self.sparks[proportion.to_usize().unwrap()]
    }
}

pub struct ImageTheme {
    min : f64,
    max : f64,
    width : usize,
    height : usize,
    image : Vec<u8>,
}

impl ImageTheme {
    fn new(width: usize, height: usize) -> ImageTheme {
        return ImageTheme {
            min: 0.0, max: 0.0,
            width: width, height: height,
            image: vec![0; (width * height * 4) as usize]
        }
    }

    fn draw_number(&mut self, total_numbers : usize, pos: usize, num: f64) {
        let segment_size = self.width / total_numbers;

        let (min, max) = self.minmax();
        let x = (segment_size * pos) as f64;
        let y = (self.height as f64 / (max - min)) * (num - min);

        let x_i : usize = x as usize;
        let y_i : usize =
            if y >= (self.height as f64) {
                0
            } else if y < 1.0 {
                self.height - 1
            } else {
                self.height - (y as usize)
            };

        let colours = [
            [170u8, 60, 57, 255,],
            [255u8, 218, 41, 255,],
            [54u8, 186, 46, 255,]
            ];
        let proportion = (num - min) / (max - min);
        let mut proportion = (colours.len() as f64) * proportion;
        if proportion == colours.len() as f64 {
            proportion = proportion - 1.0;
        }
        let colour = colours[proportion.to_usize().unwrap()];
        let h = self.height;
        self.fill_bar(x_i, x_i + (segment_size - 1), y_i, h - 0, &colour);
    }

    fn fill_bar(&mut self, x1: usize, x2 : usize, y1 : usize, y2: usize, colour: &[u8; 4] ) {
        assert!(x1 < x2);
        assert!(y1 < y2);

        for x in (x1 .. x2) {
            for y in (y1 .. y2) {
                let pixel_pos = (y * self.width + x) * 4;
                for (p, c) in colour.iter().enumerate() {
                    self.image[pixel_pos + p] = *c;
                }
            }
        }
    }

}

impl SparkTheme for ImageTheme {
    fn start(&mut self, min : f64, max : f64) {
        self.min = min;
        self.max = max;
    }

    fn minmax(&self) -> (f64, f64) {
        (self.min, self.max)
    }

    fn spark(&mut self, pos: usize, length: usize, num : f64) -> &str {
        self.draw_number(length, pos, num);
        return "";
    }

    fn end(&mut self) {
        let png_mem = lodepng::encode_memory(&self.image, self.width, self.height, lodepng::LCT_RGBA, 8)
            .ok()
            .expect("Failed to generate PNG");

        // This conversion to a Vec is wasteful, but I don't know how to easily iterate on a CVec
        // or get a u8 array from it.
        let mut png_array : Vec<u8> = Vec::with_capacity(png_mem.len());
        for i in (0..png_mem.len()) {
            png_array.push(*png_mem.get(i).unwrap());
        }

        // Dump the png to term using iTerm2 extension:
        // http://www.iterm2.com/images.html
        print!("\n\x1B]1337;File=inline=1:");
        print!("{}", png_array.to_base64(base64::STANDARD));
        println!("\x07");

        // TODO: generally support dumping spark lines to files
        //let path = &Path::new("write_test.png");
        // code assumes we are 4 bytes ber pixel
        //let result = lodepng::encode_file(path, &self.image, self.width, self.height, lodepng::LCT_RGBA, 8);

        //match result {
            //Ok(_) => println!("ok"),
            //Err(e) => println!("{}", e),
        //}
    }
}

fn colorise(x : &str) -> String {
    let reset = "\x1B[0m";

    match x {
        "▁"|"▂" => "\x1B[0;32m".to_owned() + x + reset,
        "▃"|"▄" => "\x1B[0;33m".to_owned() + x + reset,
        "▅"|"▆" => "\x1B[0;33m".to_owned() + x + reset,
        "▇"|"█" => "\x1B[0;31m".to_owned() + x + reset,
        _ => x.to_string(),
    }
}

pub fn select_sparkline(st : &str) -> Box<SparkTheme> {
    let sparks = "▁▂▃▄▅▆▇█";
    match st {
        "colour" => {
            let spark_chars : Vec<String> = sparks.chars().map(|x| colorise(&x.to_string())).collect();
            Box::new(MappingTheme {
                min: 0.0, max: 0.0,
                sparks: spark_chars
            })
        },
        "png" => {
            Box::new(ImageTheme::new(200, 30))
        },
        _ => {
            Box::new(MappingTheme {
                min: 0.0, max: 0.0,
                sparks: sparks.chars().map(|x| x.to_string()).collect()
            })
        },
    }
}

#[test]
fn test_sparkline_mapping() {
    let (min, max) : (f64, f64) = (0.0, 10.0);
    let values = vec![2.0, 3.0, 2.0, 6.0, 9.0];
    let expected = "▂▃▂▅█".to_owned();
    let mut sparky = select_sparkline("classic");

    sparky.start(min, max);
    for (num, compare) in values.iter().zip(expected.chars()) {
        let s : &str= sparky.spark(*num);
        println!("{}", num);
        assert_eq!(s, &compare.to_string());
    }

}
<commit_msg>update test<commit_after>extern crate num;
extern crate lodepng;
extern crate rustc_serialize;

use num::traits::{ Float, ToPrimitive };

//use std::path::Path;
use rustc_serialize::base64::{self, ToBase64};

pub fn parse_numbers (args : &[String]) -> Vec<f64> {
    args.iter().enumerate().map(|(i, x)| {
        x.parse::<f64>().ok().expect(&format!("Argument \"{}\" was not a number :(", args[i]))
    }).collect()
}

/// Find the minimum and maximum for a slice of Float values.
///
/// ```
/// use sparkline::*;
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], None, None);
/// assert_eq!(x, 0.0);
/// assert_eq!(y, 2.0);
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], Some(-1.0), None);
/// assert_eq!(x, -1.0);
/// assert_eq!(y, 2.0);
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], Some(1.0), None);
/// assert_eq!(x, 1.0);
/// assert_eq!(y, 2.0);
/// let (x, y) = min_max_for_data(&vec![0.0, 1.0, 2.0], None, Some(1.5));
/// assert_eq!(x, 0.0);
/// assert_eq!(y, 1.5);
/// ```
pub fn min_max_for_data<T>(numbers: &[T], min_opt: Option<T>, max_opt: Option<T>) -> (f64, f64) where T: Float {
    let max = match max_opt {
        Some(m) => m,
        None => numbers.iter().fold(T::min_value(), |a, b| a.max(*b)),
    };
    let min = match min_opt {
        Some(m) => m,
        None => numbers.iter().fold(T::max_value(), |a, b| a.min(*b)),
    };
    (min.to_f64().unwrap(), max.to_f64().unwrap())
}

pub trait SparkTheme {
    fn start(&mut self, min : f64, max : f64);
    fn spark(&mut self, pos: usize, length: usize, num : f64) -> &str;
    fn end(&mut self) {}

    fn minmax(&self) -> (f64, f64);
    fn proportion(&self, num : f64) -> f64 {
        let (min, max) = self.minmax();

        (num - min) / (max - min)
    }
}

pub struct MappingTheme {
    pub sparks : Vec<String>,
    min : f64,
    max : f64,
}

impl SparkTheme for MappingTheme {
    fn start(&mut self, min : f64, max : f64) {
        self.min = min;
        self.max = max;
    }

    fn minmax(&self) -> (f64, f64) {
        (self.min, self.max)
    }

    fn spark(&mut self, _pos: usize, _length: usize, num : f64) -> &str {
        let increments = self.sparks.len() as f64;

        let mut proportion = (increments) * self.proportion(num);

        // If num == max, then proportion will be out of bounds, so drop one
        if proportion == increments {
            proportion = proportion - 1.0;
        }

        &self.sparks[proportion.to_usize().unwrap()]
    }
}

pub struct ImageTheme {
    min : f64,
    max : f64,
    width : usize,
    height : usize,
    image : Vec<u8>,
}

impl ImageTheme {
    fn new(width: usize, height: usize) -> ImageTheme {
        return ImageTheme {
            min: 0.0, max: 0.0,
            width: width, height: height,
            image: vec![0; (width * height * 4) as usize]
        }
    }

    fn draw_number(&mut self, total_numbers : usize, pos: usize, num: f64) {
        let segment_size = self.width / total_numbers;

        let (min, max) = self.minmax();
        let x = (segment_size * pos) as f64;
        let y = (self.height as f64 / (max - min)) * (num - min);

        let x_i : usize = x as usize;
        let y_i : usize =
            if y >= (self.height as f64) {
                0
            } else if y < 1.0 {
                self.height - 1
            } else {
                self.height - (y as usize)
            };

        let colours = [
            [170u8, 60, 57, 255,],
            [255u8, 218, 41, 255,],
            [54u8, 186, 46, 255,]
            ];
        let proportion = (num - min) / (max - min);
        let mut proportion = (colours.len() as f64) * proportion;
        if proportion == colours.len() as f64 {
            proportion = proportion - 1.0;
        }
        let colour = colours[proportion.to_usize().unwrap()];
        let h = self.height;
        self.fill_bar(x_i, x_i + (segment_size - 1), y_i, h - 0, &colour);
    }

    fn fill_bar(&mut self, x1: usize, x2 : usize, y1 : usize, y2: usize, colour: &[u8; 4] ) {
        assert!(x1 < x2);
        assert!(y1 < y2);

        for x in (x1 .. x2) {
            for y in (y1 .. y2) {
                let pixel_pos = (y * self.width + x) * 4;
                for (p, c) in colour.iter().enumerate() {
                    self.image[pixel_pos + p] = *c;
                }
            }
        }
    }

}

impl SparkTheme for ImageTheme {
    fn start(&mut self, min : f64, max : f64) {
        self.min = min;
        self.max = max;
    }

    fn minmax(&self) -> (f64, f64) {
        (self.min, self.max)
    }

    fn spark(&mut self, pos: usize, length: usize, num : f64) -> &str {
        self.draw_number(length, pos, num);
        return "";
    }

    fn end(&mut self) {
        let png_mem = lodepng::encode_memory(&self.image, self.width, self.height, lodepng::LCT_RGBA, 8)
            .ok()
            .expect("Failed to generate PNG");

        // This conversion to a Vec is wasteful, but I don't know how to easily iterate on a CVec
        // or get a u8 array from it.
        let mut png_array : Vec<u8> = Vec::with_capacity(png_mem.len());
        for i in (0..png_mem.len()) {
            png_array.push(*png_mem.get(i).unwrap());
        }

        // Dump the png to term using iTerm2 extension:
        // http://www.iterm2.com/images.html
        print!("\n\x1B]1337;File=inline=1:");
        print!("{}", png_array.to_base64(base64::STANDARD));
        println!("\x07");

        // TODO: generally support dumping spark lines to files
        //let path = &Path::new("write_test.png");
        // code assumes we are 4 bytes ber pixel
        //let result = lodepng::encode_file(path, &self.image, self.width, self.height, lodepng::LCT_RGBA, 8);

        //match result {
            //Ok(_) => println!("ok"),
            //Err(e) => println!("{}", e),
        //}
    }
}

fn colorise(x : &str) -> String {
    let reset = "\x1B[0m";

    match x {
        "▁"|"▂" => "\x1B[0;32m".to_owned() + x + reset,
        "▃"|"▄" => "\x1B[0;33m".to_owned() + x + reset,
        "▅"|"▆" => "\x1B[0;33m".to_owned() + x + reset,
        "▇"|"█" => "\x1B[0;31m".to_owned() + x + reset,
        _ => x.to_string(),
    }
}

pub fn select_sparkline(st : &str) -> Box<SparkTheme> {
    let sparks = "▁▂▃▄▅▆▇█";
    match st {
        "colour" => {
            let spark_chars : Vec<String> = sparks.chars().map(|x| colorise(&x.to_string())).collect();
            Box::new(MappingTheme {
                min: 0.0, max: 0.0,
                sparks: spark_chars
            })
        },
        "png" => {
            Box::new(ImageTheme::new(200, 30))
        },
        _ => {
            Box::new(MappingTheme {
                min: 0.0, max: 0.0,
                sparks: sparks.chars().map(|x| x.to_string()).collect()
            })
        },
    }
}

#[test]
fn test_sparkline_mapping() {
    let (min, max) : (f64, f64) = (0.0, 10.0);
    let values = vec![2.0, 3.0, 2.0, 6.0, 9.0];
    let expected = "▂▃▂▅█".to_owned();
    let mut sparky = select_sparkline("classic");

    sparky.start(min, max);
    let length = values.len();
    for (pos, (num, compare)) in values.iter().zip(expected.chars()).enumerate() {
        let s : &str= sparky.spark(pos, length, *num);
        println!("{}", num);
        assert_eq!(s, &compare.to_string());
    }

}
<|endoftext|>


<commit_before>#![crate_name = "orbtk"]
#![crate_type = "lib"]
// #![deny(warnings)]
#![feature(const_fn)]

extern crate dces;

pub use dces::prelude::*;

extern crate cssparser;
#[macro_use]
extern crate lazy_static;

pub use application::*;
pub use backend::*;
pub use enums::*;
pub use event::*;
pub use layout_object::*;
pub use render_object::*;
pub use properties::*;
pub use systems::*;
pub use theme::{Selector, Theme, DEFAULT_THEME_CSS, LIGHT_THEME_CSS};
pub use widget::*;

pub mod application;
pub mod backend;
pub mod enums;
pub mod event;
pub mod layout_object;
pub mod render_object;
pub mod properties;
pub mod systems;
pub mod theme;
pub mod widget;

extern crate orbclient;
extern crate orbfont;
extern crate orbimage;
pub use orbclient::color::Color;<commit_msg>Update lib.rs<commit_after>#![crate_name = "orbtk"]
#![crate_type = "lib"]
// #![deny(warnings)]

extern crate dces;

pub use dces::prelude::*;

extern crate cssparser;
#[macro_use]
extern crate lazy_static;

pub use application::*;
pub use backend::*;
pub use enums::*;
pub use event::*;
pub use layout_object::*;
pub use render_object::*;
pub use properties::*;
pub use systems::*;
pub use theme::{Selector, Theme, DEFAULT_THEME_CSS, LIGHT_THEME_CSS};
pub use widget::*;

pub mod application;
pub mod backend;
pub mod enums;
pub mod event;
pub mod layout_object;
pub mod render_object;
pub mod properties;
pub mod systems;
pub mod theme;
pub mod widget;

extern crate orbclient;
extern crate orbfont;
extern crate orbimage;
pub use orbclient::color::Color;<|endoftext|>


<commit_before>#![feature(phase)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate serialize;

use std::cell::RefCell;
use std::collections::HashMap;
use std::io::{BufferedReader, InvalidInput, IoError, IoResult};
use std::vec::Vec;
use conn::{Connection, connect, send};
use data::{Config, Message};

pub mod conn;
pub mod data;

pub struct Bot<'a> {
    pub conn: Connection,
    pub config: Config,
    process: RefCell<|&Bot, &str, &str, &[&str]|:'a -> IoResult<()>>,
    pub chanlists: HashMap<String, Vec<String>>,
}

impl<'a> Bot<'a> {
    pub fn new(process: |&Bot, &str, &str, &[&str]|:'a -> IoResult<()>) -> IoResult<Bot<'a>> {
        let config = try!(Config::load());
        let conn = try!(connect(config.server.as_slice(), config.port));
        Ok(Bot {
            conn: conn,
            config: config,
            process: RefCell::new(process),
            chanlists: HashMap::new(),
        })
    }

    pub fn send_nick(&self, nick: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "NICK", [nick]))
    }

    pub fn send_user(&self, username: &str, real_name: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "USER", [username, "0", "*", real_name]))
    }

    pub fn send_join(&self, chan: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "JOIN", [chan.as_slice()]))
    }

    pub fn send_invite(&self, person: &str, chan: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "INVITE", [person.as_slice(), chan.as_slice()]))
    }

    pub fn send_privmsg(&self, chan: &str, msg: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "PRIVMSG", [chan.as_slice(), msg.as_slice()]))
    }

    pub fn identify(&self) -> IoResult<()> {
        self.send_nick(self.config.nickname.as_slice());
        self.send_user(self.config.username.as_slice(), self.config.realname.as_slice())
    }

    pub fn output(&mut self) {
        let mut reader = { let Connection(ref tcp) = self.conn; BufferedReader::new(tcp.clone()) };
        for line in reader.lines() {
            match line {
                Ok(ln) => {
                    let (source, command, args) = process(ln.as_slice()).unwrap();
                    self.handle_command(source, command, args.as_slice());
                    println!("{}", ln)
                },
                Err(e) => println!("Shit, you're fucked! {}", e),
            }
        }
    }

    fn handle_command(&mut self, source: &str, command: &str, args: &[&str]) -> IoResult<()> {
        match (command, args) {
            ("PING", [msg]) => {
                try!(send(&self.conn, Message::new(None, "PONG", [msg])));
            },
            ("376", _) => { // End of MOTD
                for chan in self.config.channels.iter() {
                    try!(self.send_join(chan.as_slice()));
                }
            },
            ("422", _) => { // Missing MOTD
                for chan in self.config.channels.iter() {
                    try!(self.send_join(chan.as_slice()));
                }
            },
            ("353", [_, _, chan, users]) => { // /NAMES
                for user in users.split_str(" ") {
                    if !match self.chanlists.find_mut(&String::from_str(chan)) {
                        Some(vec) => {
                            vec.push(String::from_str(user));
                            true
                        },
                        None => false,
                    } {
                        self.chanlists.insert(String::from_str(chan), vec!(String::from_str(user)));
                    }
                }
            },
            ("JOIN", [chan]) => {
                match self.chanlists.find_mut(&String::from_str(chan)) {
                    Some(vec) => {
                        match source.find('!') {
                            Some(i) => vec.push(String::from_str(source.slice_to(i))),
                            None => (),
                        };
                    },
                    None => (),
                }
            },
            ("PART", [chan, _]) => {
                match self.chanlists.find_mut(&String::from_str(chan)) {
                    Some(vec) => {
                        match source.find('!') {
                            Some(i) => {
                                match vec.as_slice().position_elem(&String::from_str(source.slice_to(i))) {
                                    Some(n) => {
                                        vec.swap_remove(n);
                                    },
                                    None => (),
                                };
                            },
                            None => (),
                        };
                    },
                    None => (),
                }
            },
            _ => {
                (*self.process.borrow_mut().deref_mut())(self, source, command, args);
            },
        };
        Ok(())
    }
}

fn process(msg: &str) -> IoResult<(&str, &str, Vec<&str>)> {
    let reg = regex!(r"^(?::([^ ]+) )?([^ ]+)(.*)");
    let cap = match reg.captures(msg) {
        Some(x) => x,
        None => return Err(IoError {
            kind: InvalidInput,
            desc: "Failed to parse line",
            detail: None,
        }),
    };
    let source = cap.at(1);
    let command = cap.at(2);
    let args = parse_args(cap.at(3));
    Ok((source, command, args))
}

fn parse_args(line: &str) -> Vec<&str> {
    let reg = regex!(r" ([^: ]+)| :([^\r\n]*)[\r\n]*$");
    reg.captures_iter(line).map(|cap| {
        match cap.at(1) {
            "" => cap.at(2),
            x => x,
        }
    }).collect()
}

#[test]
fn process_line_test() {
    let res = process(":flare.to.ca.fyrechat.net 353 pickles = #pickles :pickles awe\r\n").unwrap();
    let (source, command, args) = res;
    assert_eq!(source, "flare.to.ca.fyrechat.net");
    assert_eq!(command, "353");
    assert_eq!(args, vec!["pickles", "=", "#pickles", "pickles awe"]);

    let res = process("PING :flare.to.ca.fyrechat.net\r\n").unwrap();
    let (source, command, args) = res;
    assert_eq!(source, "");
    assert_eq!(command, "PING");
    assert_eq!(args, vec!["flare.to.ca.fyrechat.net"]);
}

#[test]
fn process_args_test() {
    let res = parse_args("PRIVMSG #vana :hi");
    assert_eq!(res, vec!["#vana", "hi"])
}
<commit_msg>Added method to set topic of a channel.<commit_after>#![feature(phase)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate serialize;

use std::cell::RefCell;
use std::collections::HashMap;
use std::io::{BufferedReader, InvalidInput, IoError, IoResult};
use std::vec::Vec;
use conn::{Connection, connect, send};
use data::{Config, Message};

pub mod conn;
pub mod data;

pub struct Bot<'a> {
    pub conn: Connection,
    pub config: Config,
    process: RefCell<|&Bot, &str, &str, &[&str]|:'a -> IoResult<()>>,
    pub chanlists: HashMap<String, Vec<String>>,
}

impl<'a> Bot<'a> {
    pub fn new(process: |&Bot, &str, &str, &[&str]|:'a -> IoResult<()>) -> IoResult<Bot<'a>> {
        let config = try!(Config::load());
        let conn = try!(connect(config.server.as_slice(), config.port));
        Ok(Bot {
            conn: conn,
            config: config,
            process: RefCell::new(process),
            chanlists: HashMap::new(),
        })
    }

    pub fn send_nick(&self, nick: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "NICK", [nick]))
    }

    pub fn send_user(&self, username: &str, real_name: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "USER", [username, "0", "*", real_name]))
    }

    pub fn send_join(&self, chan: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "JOIN", [chan.as_slice()]))
    }

    pub fn send_topic(&self, chan: &str, topic: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "TOPIC", [chan.as_slice(), topic.as_slice()]))
    }

    pub fn send_invite(&self, person: &str, chan: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "INVITE", [person.as_slice(), chan.as_slice()]))
    }

    pub fn send_privmsg(&self, chan: &str, msg: &str) -> IoResult<()> {
        send(&self.conn, Message::new(None, "PRIVMSG", [chan.as_slice(), msg.as_slice()]))
    }

    pub fn identify(&self) -> IoResult<()> {
        self.send_nick(self.config.nickname.as_slice());
        self.send_user(self.config.username.as_slice(), self.config.realname.as_slice())
    }

    pub fn output(&mut self) {
        let mut reader = { let Connection(ref tcp) = self.conn; BufferedReader::new(tcp.clone()) };
        for line in reader.lines() {
            match line {
                Ok(ln) => {
                    let (source, command, args) = process(ln.as_slice()).unwrap();
                    self.handle_command(source, command, args.as_slice());
                    println!("{}", ln)
                },
                Err(e) => println!("Shit, you're fucked! {}", e),
            }
        }
    }

    fn handle_command(&mut self, source: &str, command: &str, args: &[&str]) -> IoResult<()> {
        match (command, args) {
            ("PING", [msg]) => {
                try!(send(&self.conn, Message::new(None, "PONG", [msg])));
            },
            ("376", _) => { // End of MOTD
                for chan in self.config.channels.iter() {
                    try!(self.send_join(chan.as_slice()));
                }
            },
            ("422", _) => { // Missing MOTD
                for chan in self.config.channels.iter() {
                    try!(self.send_join(chan.as_slice()));
                }
            },
            ("353", [_, _, chan, users]) => { // /NAMES
                for user in users.split_str(" ") {
                    if !match self.chanlists.find_mut(&String::from_str(chan)) {
                        Some(vec) => {
                            vec.push(String::from_str(user));
                            true
                        },
                        None => false,
                    } {
                        self.chanlists.insert(String::from_str(chan), vec!(String::from_str(user)));
                    }
                }
            },
            ("JOIN", [chan]) => {
                match self.chanlists.find_mut(&String::from_str(chan)) {
                    Some(vec) => {
                        match source.find('!') {
                            Some(i) => vec.push(String::from_str(source.slice_to(i))),
                            None => (),
                        };
                    },
                    None => (),
                }
            },
            ("PART", [chan, _]) => {
                match self.chanlists.find_mut(&String::from_str(chan)) {
                    Some(vec) => {
                        match source.find('!') {
                            Some(i) => {
                                match vec.as_slice().position_elem(&String::from_str(source.slice_to(i))) {
                                    Some(n) => {
                                        vec.swap_remove(n);
                                    },
                                    None => (),
                                };
                            },
                            None => (),
                        };
                    },
                    None => (),
                }
            },
            _ => {
                (*self.process.borrow_mut().deref_mut())(self, source, command, args);
            },
        };
        Ok(())
    }
}

fn process(msg: &str) -> IoResult<(&str, &str, Vec<&str>)> {
    let reg = regex!(r"^(?::([^ ]+) )?([^ ]+)(.*)");
    let cap = match reg.captures(msg) {
        Some(x) => x,
        None => return Err(IoError {
            kind: InvalidInput,
            desc: "Failed to parse line",
            detail: None,
        }),
    };
    let source = cap.at(1);
    let command = cap.at(2);
    let args = parse_args(cap.at(3));
    Ok((source, command, args))
}

fn parse_args(line: &str) -> Vec<&str> {
    let reg = regex!(r" ([^: ]+)| :([^\r\n]*)[\r\n]*$");
    reg.captures_iter(line).map(|cap| {
        match cap.at(1) {
            "" => cap.at(2),
            x => x,
        }
    }).collect()
}

#[test]
fn process_line_test() {
    let res = process(":flare.to.ca.fyrechat.net 353 pickles = #pickles :pickles awe\r\n").unwrap();
    let (source, command, args) = res;
    assert_eq!(source, "flare.to.ca.fyrechat.net");
    assert_eq!(command, "353");
    assert_eq!(args, vec!["pickles", "=", "#pickles", "pickles awe"]);

    let res = process("PING :flare.to.ca.fyrechat.net\r\n").unwrap();
    let (source, command, args) = res;
    assert_eq!(source, "");
    assert_eq!(command, "PING");
    assert_eq!(args, vec!["flare.to.ca.fyrechat.net"]);
}

#[test]
fn process_args_test() {
    let res = parse_args("PRIVMSG #vana :hi");
    assert_eq!(res, vec!["#vana", "hi"])
}
<|endoftext|>


<commit_before>#![feature(no_std)]
#![feature(core)]
#![no_std]

#![crate_name = "klogger"]
#![crate_type = "lib"]

#[macro_use]
extern crate core;

use core::prelude::*;
use core::atomic;
use core::fmt;

pub mod macros;

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(target_arch="x86_64")]
#[macro_use]
extern crate x86;

#[cfg(target_arch="x86_64")]
#[path="arch/x86.rs"]
mod arch;

pub struct Writer;

impl Writer {
    /// Obtain a logger for the specified module.
    pub fn get(module: &str) -> Writer {
        use core::fmt::Write;
        let mut ret = Writer;
        write!(&mut ret, "[{}] ", module);
        ret
    }
}

impl ::core::ops::Drop for Writer {
    /// Release the logger.
    fn drop(&mut self) {
        use core::fmt::Write;
        write!(self, "\n");
    }
}

impl fmt::Write for Writer {
    /// Write stuff to serial out.
    fn write_str(&mut self, s: &str) -> fmt::Result {
        unsafe { arch::puts(s); }
        Ok(())
    }
}
<commit_msg>Update klogger features.<commit_after>#![feature(no_std)]
#![feature(core)]
#![feature(core_prelude)]
#![feature(core_str_ext)]
#![no_std]

#![crate_name = "klogger"]
#![crate_type = "lib"]

#[macro_use]
extern crate core;

use core::prelude::*;
use core::atomic;
use core::fmt;

pub mod macros;

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(target_arch="x86_64")]
#[macro_use]
extern crate x86;

#[cfg(target_arch="x86_64")]
#[path="arch/x86.rs"]
mod arch;

pub struct Writer;

impl Writer {
    /// Obtain a logger for the specified module.
    pub fn get(module: &str) -> Writer {
        use core::fmt::Write;
        let mut ret = Writer;
        write!(&mut ret, "[{}] ", module);
        ret
    }
}

impl ::core::ops::Drop for Writer {
    /// Release the logger.
    fn drop(&mut self) {
        use core::fmt::Write;
        write!(self, "\n");
    }
}

impl fmt::Write for Writer {
    /// Write stuff to serial out.
    fn write_str(&mut self, s: &str) -> fmt::Result {
        unsafe { arch::puts(s); }
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate soio;
extern crate threading;
extern crate chrono;
extern crate regex;
extern crate rustls;

pub use server::Server;
pub use http::Http;
pub use http::Request;
pub use http::Response;
pub use micro::Micro;
pub use micro::Group;

pub mod server;
pub mod http;
pub mod micro;
pub mod util;
pub mod error;
<commit_msg>export error::Error<commit_after>extern crate soio;
extern crate threading;
extern crate chrono;
extern crate regex;
extern crate rustls;

pub use server::Server;
pub use http::Http;
pub use http::Request;
pub use http::Response;
pub use micro::Micro;
pub use micro::Group;
pub use error::Error;

pub mod server;
pub mod http;
pub mod micro;
pub mod util;
pub mod error;
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://docs.rs/twitter-stream/0.10.0-alpha.6")]

/*!
# Twitter Stream

A library for listening on Twitter Streaming API.

## Usage

Add this to your `Cargo.toml`:

```toml
[dependencies]
futures = "0.3"
tokio = { version = "0.2", features = ["macros"] }
twitter-stream = "=0.10.0-alpha.6"
```

## Overview

Here is a basic example that prints public mentions to @Twitter in JSON format:

```rust,no_run
use futures::prelude::*;
use twitter_stream::{Token, TwitterStream};

# #[tokio::main]
# async fn main() {
let token = Token::new("consumer_key", "consumer_secret", "access_key", "access_secret");

TwitterStream::track("@Twitter", &token)
    .try_flatten_stream()
    .try_for_each(|json| {
        println!("{}", json);
        future::ok(())
    })
    .await
    .unwrap();
# }
```

See the [`TwitterStream`] type documentation for details.

## Streaming messages

`TwitterStream` yields the raw JSON strings returned by the Streaming API. Each string value
contains exactly one JSON value.

The underlying Streaming API [sends a blank line][stalls] every 30 seconds as a "keep-alive" signal,
but `TwitterStream` discards it so that you can always expect to yield a valid JSON string.
On the other hand, this means that you cannot use the blank line to set a timeout on `Stream`-level.
If you want the stream to time out on network stalls, set a timeout on the underlying
HTTP connector, instead of the `Stream` (see the [`timeout` example] in the crate's repository
for details).

[stalls]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/connecting#stalls
[`timeout` example]: https://github.com/tesaguri/twitter-stream-rs/blob/v0.10.0-alpha.6/examples/timeout.rs

The JSON string usually, but not always, represents a [Tweet] object. When deserializing the JSON
string, you should be able to handle any kind of JSON value. A possible implementation of
deserialization would be like the following:

[Tweet]: https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/tweet-object

```rust
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum StreamMessage {
    Tweet(Tweet),
    // Discards anything other than a Tweet.
    // You can handle other message types as well by adding correspoiding variants.
    Other(de::IgnoredAny),
}
```

The [`echo_bot` example] in the crate's repository shows an example of a `StreamMessage`
implementation.

[`echo_bot` example]: https://github.com/tesaguri/twitter-stream-rs/blob/v0.10.0-alpha.6/examples/echo_bot.rs

See the [Twitter Developers Documentation][message-types] for the types and formats of the JSON
messages.

[message-types]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/streaming-message-types
*/

#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(intra_doc_link_resolution_failure)]
#![warn(missing_docs)]

#[macro_use]
mod util;

pub mod builder;
pub mod error;
#[cfg(feature = "hyper")]
#[cfg_attr(docsrs, doc(cfg(feature = "hyper")))]
pub mod hyper;
pub mod service;

mod gzip;
mod token;

pub use oauth::Credentials;

pub use crate::builder::Builder;
pub use crate::error::Error;
pub use crate::token::Token;

use std::future::Future;
use std::pin::Pin;
use std::str;
use std::task::{Context, Poll};

use bytes::Bytes;
use futures_core::Stream;
use futures_util::ready;
use http::header::CONTENT_ENCODING;
use http::response::Parts;
use http::Response;
use http::StatusCode;
use http_body::Body;
use pin_project_lite::pin_project;

use crate::gzip::MaybeGzip;
use crate::util::{HttpBodyAsStream, Lines};

pin_project! {
    /// A future returned by constructor methods which resolves to a [`TwitterStream`].
    pub struct FutureTwitterStream<F> {
        #[pin]
        response: F,
    }
}

pin_project! {
    /// A listener for Twitter Streaming API, yielding JSON strings returned from the API.
    pub struct TwitterStream<B: Body> {
        #[pin]
        inner: Lines<MaybeGzip<HttpBodyAsStream<B>>>,
    }
}

impl<B: Body> TwitterStream<B> {
    /// Creates a `Builder` for `TwitterStream`.
    pub fn builder<'a, C, A>(token: Token<C, A>) -> Builder<'a, Token<C, A>>
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token)
    }
}

#[cfg(feature = "hyper")]
impl crate::hyper::TwitterStream {
    /// Connect to the filter stream, yielding Tweets from the users specified by `follow` argument.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).follow(follow).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn follow<C, A>(follow: &[u64], token: &Token<C, A>) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).follow(follow).listen()
    }

    /// Connect to the filter stream, yielding Tweets that matches the query specified by
    /// `track` argument.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).track(track).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn track<C, A>(track: &str, token: &Token<C, A>) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).track(track).listen()
    }

    /// Connect to the filter stream, yielding geolocated Tweets falling within the specified
    /// bounding boxes.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).locations(locations).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn locations<C, A>(
        locations: &[builder::BoundingBox],
        token: &Token<C, A>,
    ) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).locations(locations).listen()
    }

    /// Connect to the sample stream, yielding a "small random sample" of all public Tweets.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn sample<C, A>(token: &Token<C, A>) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).listen()
    }
}

impl<F, B, E> Future for FutureTwitterStream<F>
where
    F: Future<Output = Result<Response<B>, E>>,
    B: Body,
{
    type Output = Result<TwitterStream<B>, Error<E>>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let res = match ready!(self.project().response.poll(cx)) {
            Ok(res) => res,
            Err(e) => return Poll::Ready(Err(Error::Service(e))),
        };
        let (parts, body) = res.into_parts();
        let Parts {
            status, headers, ..
        } = parts;

        if StatusCode::OK != status {
            return Poll::Ready(Err(Error::Http(status)));
        }

        let use_gzip = headers
            .get_all(CONTENT_ENCODING)
            .iter()
            .any(|e| e == "gzip");
        let inner = if use_gzip {
            Lines::new(gzip::gzip(HttpBodyAsStream::new(body)))
        } else {
            Lines::new(gzip::identity(HttpBodyAsStream::new(body)))
        };

        Poll::Ready(Ok(TwitterStream { inner }))
    }
}

impl<B> Stream for TwitterStream<B>
where
    B: Body,
{
    type Item = Result<string::String<Bytes>, Error<B::Error>>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut this = self.project();

        loop {
            let line = match ready!(this.inner.as_mut().poll_next(cx)?) {
                Some(t) => t,
                None => return std::task::Poll::Ready(None),
            };

            if line.iter().all(|&c| is_json_whitespace(c)) {
                continue;
            }

            str::from_utf8(&line).map_err(Error::Utf8)?;
            let line = unsafe {
                // Safety:
                // - We have checked above that `line` is valid as UTF-8.
                // - `Bytes` satisfies the requirements of `string::StableAsRef` trait
                // (https://github.com/carllerche/string/pull/17)
                string::String::<Bytes>::from_utf8_unchecked(line)
            };
            return Poll::Ready(Some(Ok(line)));
        }
    }
}

fn is_json_whitespace(c: u8) -> bool {
    // RFC7159 §2
    b" \t\n\r".contains(&c)
}
<commit_msg>Fix doctest failure<commit_after>#![doc(html_root_url = "https://docs.rs/twitter-stream/0.10.0-alpha.6")]

/*!
# Twitter Stream

A library for listening on Twitter Streaming API.

## Usage

Add this to your `Cargo.toml`:

```toml
[dependencies]
futures = "0.3"
tokio = { version = "0.2", features = ["macros"] }
twitter-stream = "=0.10.0-alpha.6"
```

## Overview

Here is a basic example that prints public mentions to @Twitter in JSON format:

```rust,no_run
use futures::prelude::*;
use twitter_stream::{Token, TwitterStream};

# #[tokio::main]
# async fn main() {
let token = Token::new("consumer_key", "consumer_secret", "access_key", "access_secret");

TwitterStream::track("@Twitter", &token)
    .try_flatten_stream()
    .try_for_each(|json| {
        println!("{}", json);
        future::ok(())
    })
    .await
    .unwrap();
# }
```

See the [`TwitterStream`] type documentation for details.

## Streaming messages

`TwitterStream` yields the raw JSON strings returned by the Streaming API. Each string value
contains exactly one JSON value.

The underlying Streaming API [sends a blank line][stalls] every 30 seconds as a "keep-alive" signal,
but `TwitterStream` discards it so that you can always expect to yield a valid JSON string.
On the other hand, this means that you cannot use the blank line to set a timeout on `Stream`-level.
If you want the stream to time out on network stalls, set a timeout on the underlying
HTTP connector, instead of the `Stream` (see the [`timeout` example] in the crate's repository
for details).

[stalls]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/connecting#stalls
[`timeout` example]: https://github.com/tesaguri/twitter-stream-rs/blob/v0.10.0-alpha.6/examples/timeout.rs

The JSON string usually, but not always, represents a [Tweet] object. When deserializing the JSON
string, you should be able to handle any kind of JSON value. A possible implementation of
deserialization would be like the following:

[Tweet]: https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/tweet-object

```rust
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum StreamMessage {
    Tweet(Tweet),
    // Discards anything other than a Tweet.
    // You can handle other message types as well by adding correspoiding variants.
    Other(serde::de::IgnoredAny),
}

#[derive(serde::Deserialize)]
struct Tweet { /* ... */ }
```

The [`echo_bot` example] in the crate's repository shows an example of a `StreamMessage`
implementation.

[`echo_bot` example]: https://github.com/tesaguri/twitter-stream-rs/blob/v0.10.0-alpha.6/examples/echo_bot.rs

See the [Twitter Developers Documentation][message-types] for the types and formats of the JSON
messages.

[message-types]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/streaming-message-types
*/

#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(intra_doc_link_resolution_failure)]
#![warn(missing_docs)]

#[macro_use]
mod util;

pub mod builder;
pub mod error;
#[cfg(feature = "hyper")]
#[cfg_attr(docsrs, doc(cfg(feature = "hyper")))]
pub mod hyper;
pub mod service;

mod gzip;
mod token;

pub use oauth::Credentials;

pub use crate::builder::Builder;
pub use crate::error::Error;
pub use crate::token::Token;

use std::future::Future;
use std::pin::Pin;
use std::str;
use std::task::{Context, Poll};

use bytes::Bytes;
use futures_core::Stream;
use futures_util::ready;
use http::header::CONTENT_ENCODING;
use http::response::Parts;
use http::Response;
use http::StatusCode;
use http_body::Body;
use pin_project_lite::pin_project;

use crate::gzip::MaybeGzip;
use crate::util::{HttpBodyAsStream, Lines};

pin_project! {
    /// A future returned by constructor methods which resolves to a [`TwitterStream`].
    pub struct FutureTwitterStream<F> {
        #[pin]
        response: F,
    }
}

pin_project! {
    /// A listener for Twitter Streaming API, yielding JSON strings returned from the API.
    pub struct TwitterStream<B: Body> {
        #[pin]
        inner: Lines<MaybeGzip<HttpBodyAsStream<B>>>,
    }
}

impl<B: Body> TwitterStream<B> {
    /// Creates a `Builder` for `TwitterStream`.
    pub fn builder<'a, C, A>(token: Token<C, A>) -> Builder<'a, Token<C, A>>
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token)
    }
}

#[cfg(feature = "hyper")]
impl crate::hyper::TwitterStream {
    /// Connect to the filter stream, yielding Tweets from the users specified by `follow` argument.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).follow(follow).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn follow<C, A>(follow: &[u64], token: &Token<C, A>) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).follow(follow).listen()
    }

    /// Connect to the filter stream, yielding Tweets that matches the query specified by
    /// `track` argument.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).track(track).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn track<C, A>(track: &str, token: &Token<C, A>) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).track(track).listen()
    }

    /// Connect to the filter stream, yielding geolocated Tweets falling within the specified
    /// bounding boxes.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).locations(locations).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn locations<C, A>(
        locations: &[builder::BoundingBox],
        token: &Token<C, A>,
    ) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).locations(locations).listen()
    }

    /// Connect to the sample stream, yielding a "small random sample" of all public Tweets.
    ///
    /// This is a shorthand for `twitter_stream::Builder::new(token).listen()`.
    /// For more specific configurations, use [`TwitterStream::builder`] or [`Builder::new`].
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    pub fn sample<C, A>(token: &Token<C, A>) -> crate::hyper::FutureTwitterStream
    where
        C: std::borrow::Borrow<str>,
        A: std::borrow::Borrow<str>,
    {
        Builder::new(token.as_ref()).listen()
    }
}

impl<F, B, E> Future for FutureTwitterStream<F>
where
    F: Future<Output = Result<Response<B>, E>>,
    B: Body,
{
    type Output = Result<TwitterStream<B>, Error<E>>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let res = match ready!(self.project().response.poll(cx)) {
            Ok(res) => res,
            Err(e) => return Poll::Ready(Err(Error::Service(e))),
        };
        let (parts, body) = res.into_parts();
        let Parts {
            status, headers, ..
        } = parts;

        if StatusCode::OK != status {
            return Poll::Ready(Err(Error::Http(status)));
        }

        let use_gzip = headers
            .get_all(CONTENT_ENCODING)
            .iter()
            .any(|e| e == "gzip");
        let inner = if use_gzip {
            Lines::new(gzip::gzip(HttpBodyAsStream::new(body)))
        } else {
            Lines::new(gzip::identity(HttpBodyAsStream::new(body)))
        };

        Poll::Ready(Ok(TwitterStream { inner }))
    }
}

impl<B> Stream for TwitterStream<B>
where
    B: Body,
{
    type Item = Result<string::String<Bytes>, Error<B::Error>>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut this = self.project();

        loop {
            let line = match ready!(this.inner.as_mut().poll_next(cx)?) {
                Some(t) => t,
                None => return std::task::Poll::Ready(None),
            };

            if line.iter().all(|&c| is_json_whitespace(c)) {
                continue;
            }

            str::from_utf8(&line).map_err(Error::Utf8)?;
            let line = unsafe {
                // Safety:
                // - We have checked above that `line` is valid as UTF-8.
                // - `Bytes` satisfies the requirements of `string::StableAsRef` trait
                // (https://github.com/carllerche/string/pull/17)
                string::String::<Bytes>::from_utf8_unchecked(line)
            };
            return Poll::Ready(Some(Ok(line)));
        }
    }
}

fn is_json_whitespace(c: u8) -> bool {
    // RFC7159 §2
    b" \t\n\r".contains(&c)
}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate idna;

pub mod errors;

#[cfg(test)]
mod tests;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;

use errors::*;
use reqwest::IntoUrl;
use idna::domain_to_unicode;

#[derive(Debug)]
struct Suffix {
    rule: String,
    typ: Type,
}

#[derive(Debug)]
pub struct List {
    rules: HashMap<String, Vec<Suffix>>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Icann,
    Private,
}

#[derive(Debug, Clone)]
pub struct Domain {
    full: String,
    typ: Option<Type>,
    suffix: Option<String>,
    registrable: Option<String>,
}

const OFFICIAL_URL: &'static str = "https://publicsuffix.org/list/public_suffix_list.dat";

impl List {
    fn append(&mut self, rule: &str, typ: Type) -> Result<()> {
        rule.rsplit('.').next()
            .ok_or(ErrorKind::InvalidRule(rule.into()).into())
            .and_then(|tld| {
                if tld.is_empty() {
                    return Err(ErrorKind::InvalidRule(rule.into()).into());
                }
                Ok(tld)})
            .and_then(|tld| {
                self.rules.entry(tld.into()).or_insert(Vec::new())
                    .push(Suffix {
                        rule: rule.into(),
                        typ: typ,
                    });
                Ok(())
            })
    }

    fn build<R: Read>(mut reader: R) -> Result<List> {
        let mut res = String::new();
        reader.read_to_string(&mut res)?;

        let mut typ = Type::Icann;

        let mut list = List {
            rules: HashMap::new(),
        };

        for line in res.lines() {
            match line {
                line if line.contains("BEGIN ICANN DOMAINS") => { typ = Type::Icann; }
                line if line.contains("BEGIN PRIVATE DOMAINS") => { typ = Type::Private; }
                line if line.starts_with("//") => { continue; }
                line => {
                    let rule = match line.split_whitespace().next() {
                        Some(rule) => rule,
                        None => continue,
                    };
                    list.append(rule, typ)?;
                }
            }
        }

        Ok(list)
    }

    pub fn from_url<U: IntoUrl>(url: U) -> Result<List> {
        reqwest::get(url)
            .map_err(|err| ErrorKind::Request(err).into())
            .and_then(Self::build)
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<List> {
        File::open(path)
            .map_err(|err| ErrorKind::Io(err).into())
            .and_then(Self::build)
    }

    pub fn fetch() -> Result<List> {
        Self::from_url(OFFICIAL_URL)
    }

    fn find_type(&self, typ: Type) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    if suffix.typ == typ {
                        res.push(&suffix.rule);
                    }
                }
                res
            })
    }

    pub fn icann(&self) -> Vec<&String> {
        self.find_type(Type::Icann)
    }

    pub fn private(&self) -> Vec<&String> {
        self.find_type(Type::Private)
    }

    pub fn all(&self) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    res.push(&suffix.rule);
                }
                res
            })
    }
}

impl Domain {
    fn is_valid(domain: &str) -> bool {
        match domain {
            d if d.starts_with('.') => { return false; }
            _ => { true }
        }
    }

    fn find_possible_matches<'a>(domain: &str, list: &'a List) -> Result<Vec<&'a Suffix>> {
        println!("domain: {:?}", domain);
        let tld = match domain.rsplit('.').next() {
            Some(tld) => {
                if tld.is_empty() { return Ok(Vec::new()); }
                tld
            },
            None => { return Ok(Vec::new()); },
        };
        let candidates = match list.rules.get(tld) {
            Some(candidates) => candidates,
            None => { return Ok(Vec::new()); },
        };
        let candidates = candidates.iter()
            .fold(Vec::new(), |mut res, ref suffix| {
                res.push(*suffix);
                res
            });
        Ok(candidates)
    }

    fn assemble(d_labels: &Vec<&str>, s_len: usize) -> String {
        d_labels[..s_len].iter().rev()
            .map(|part| *part)
            .collect::<Vec<_>>()
            .join(".")
    }

    fn find_match(input: &str, domain: &str, domain_is_valid: bool, candidates: Vec<&Suffix>) -> Result<Domain> {
        let d_labels: Vec<&str> = domain.split('.').rev().collect();

        let mut registrable = None;
        let mut suffix = None;
        let mut typ = None;
        let mut num_labels = 0;

        if domain_is_valid {
            let no_possible_matches_found = candidates.is_empty();

            for candidate in candidates {
                let s_labels: Vec<&str> = candidate.rule.split('.').rev().collect();
                if s_labels.len() > d_labels.len() { continue; }
                for (i, label) in s_labels.iter().enumerate() {
                    if *label == d_labels[i] || *label == "*" || label.trim_left_matches('!') == d_labels[i] {
                        if i == s_labels.len()-1 {
                            if s_labels.len() >= num_labels {
                                num_labels = s_labels.len();
                                typ = Some(candidate.typ);
                                let s_len = if label.starts_with("!") {
                                    s_labels.len()-1
                                } else {
                                    s_labels.len()
                                };
                                suffix = Some(Self::assemble(&d_labels, s_len));
                                if d_labels.len() > s_len {
                                    registrable = Some(Self::assemble(&d_labels, s_len+1));
                                } else {
                                    registrable = None;
                                }
                            }
                        }
                    } else {
                        break;
                    }
                }
            }

            if suffix.is_none() && d_labels.len() > 1 && no_possible_matches_found {
                    suffix = Some(Self::assemble(&d_labels, 1));
                    registrable = Some(Self::assemble(&d_labels, 2));
            }
        }

        Ok(Domain {
            full: input.to_string(),
            typ: typ,
            suffix: suffix,
            registrable: registrable,
        })
    }

    pub fn parse(domain: &str, list: &List) -> Result<Domain> {
        let input = domain;

        let domain = input.trim().trim_right_matches('.');
        let (domain, res) = domain_to_unicode(domain);
        if let Err(errors) = res {
            return Err(ErrorKind::Uts46(errors).into());
        }

        let domain_is_valid = Domain::is_valid(input);

        let res = if domain_is_valid {
            Self::find_possible_matches(&domain, list)?
        } else {
            Vec::new()
        };

        Self::find_match(input, &domain, domain_is_valid, res)
    }

    pub fn root_domain(&self) -> Option<&String> {
        match self.registrable {
            Some(ref registrable) => Some(registrable),
            None => None,
        }
    }

    pub fn suffix(&self) -> Option<&String> {
        match self.suffix {
            Some(ref suffix) => Some(suffix),
            None => None,
        }
    }

    pub fn is_private(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => false,
                Type::Private => true,
            },
            None => false,
        }
    }

    pub fn is_icann(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => true,
                Type::Private => false,
            },
            None => false,
        }
    }
}
<commit_msg>Fix the remaining tests<commit_after>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate idna;

pub mod errors;

#[cfg(test)]
mod tests;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;

use errors::*;
use reqwest::IntoUrl;
use idna::domain_to_unicode;

#[derive(Debug)]
struct Suffix {
    rule: String,
    typ: Type,
}

#[derive(Debug)]
pub struct List {
    rules: HashMap<String, Vec<Suffix>>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum Type {
    Icann,
    Private,
}

#[derive(Debug, Clone)]
pub struct Domain {
    full: String,
    typ: Option<Type>,
    suffix: Option<String>,
    registrable: Option<String>,
}

const OFFICIAL_URL: &'static str = "https://publicsuffix.org/list/public_suffix_list.dat";

impl List {
    fn append(&mut self, rule: &str, typ: Type) -> Result<()> {
        rule.rsplit('.').next()
            .ok_or(ErrorKind::InvalidRule(rule.into()).into())
            .and_then(|tld| {
                if tld.is_empty() {
                    return Err(ErrorKind::InvalidRule(rule.into()).into());
                }
                Ok(tld)})
            .and_then(|tld| {
                self.rules.entry(tld.into()).or_insert(Vec::new())
                    .push(Suffix {
                        rule: rule.into(),
                        typ: typ,
                    });
                Ok(())
            })
    }

    fn build<R: Read>(mut reader: R) -> Result<List> {
        let mut res = String::new();
        reader.read_to_string(&mut res)?;

        let mut typ = Type::Icann;

        let mut list = List {
            rules: HashMap::new(),
        };

        for line in res.lines() {
            match line {
                line if line.contains("BEGIN ICANN DOMAINS") => { typ = Type::Icann; }
                line if line.contains("BEGIN PRIVATE DOMAINS") => { typ = Type::Private; }
                line if line.starts_with("//") => { continue; }
                line => {
                    let rule = match line.split_whitespace().next() {
                        Some(rule) => rule,
                        None => continue,
                    };
                    list.append(rule, typ)?;
                }
            }
        }

        Ok(list)
    }

    pub fn from_url<U: IntoUrl>(url: U) -> Result<List> {
        reqwest::get(url)
            .map_err(|err| ErrorKind::Request(err).into())
            .and_then(Self::build)
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<List> {
        File::open(path)
            .map_err(|err| ErrorKind::Io(err).into())
            .and_then(Self::build)
    }

    pub fn fetch() -> Result<List> {
        Self::from_url(OFFICIAL_URL)
    }

    fn find_type(&self, typ: Type) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    if suffix.typ == typ {
                        res.push(&suffix.rule);
                    }
                }
                res
            })
    }

    pub fn icann(&self) -> Vec<&String> {
        self.find_type(Type::Icann)
    }

    pub fn private(&self) -> Vec<&String> {
        self.find_type(Type::Private)
    }

    pub fn all(&self) -> Vec<&String> {
        self.rules.values()
            .fold(Vec::new(), |mut res, ref suffices| {
                for suffix in *suffices {
                    res.push(&suffix.rule);
                }
                res
            })
    }
}

impl Domain {
    fn is_valid(domain: &str) -> bool {
        match domain {
            d if d.starts_with('.') => { return false; }
            _ => { true }
        }
    }

    fn find_possible_matches<'a>(domain: &str, list: &'a List) -> Result<Vec<&'a Suffix>> {
        println!("domain: {:?}", domain);
        let tld = match domain.rsplit('.').next() {
            Some(tld) => {
                if tld.is_empty() { return Ok(Vec::new()); }
                tld
            },
            None => { return Ok(Vec::new()); },
        };
        let candidates = match list.rules.get(tld) {
            Some(candidates) => candidates,
            None => { return Ok(Vec::new()); },
        };
        let candidates = candidates.iter()
            .fold(Vec::new(), |mut res, ref suffix| {
                res.push(*suffix);
                res
            });
        Ok(candidates)
    }

    fn assemble(input: &str, s_len: usize) -> String {
        let domain = input.to_lowercase();

        let d_labels: Vec<&str> = domain
            .trim_right_matches('.')
            .split('.').rev().collect();

        (&d_labels[..s_len]).iter().rev()
            .map(|part| *part)
            .collect::<Vec<_>>()
            .join(".")
    }

    fn find_match(input: &str, domain: &str, domain_is_valid: bool, candidates: Vec<&Suffix>) -> Result<Domain> {
        let d_labels: Vec<&str> = domain.split('.').rev().collect();

        let mut registrable = None;
        let mut suffix = None;
        let mut typ = None;
        let mut num_labels = 0;

        if domain_is_valid {
            let no_possible_matches_found = candidates.is_empty();

            for candidate in candidates {
                let s_labels: Vec<&str> = candidate.rule.split('.').rev().collect();
                if s_labels.len() > d_labels.len() { continue; }
                for (i, label) in s_labels.iter().enumerate() {
                    if *label == d_labels[i] || *label == "*" || label.trim_left_matches('!') == d_labels[i] {
                        if i == s_labels.len()-1 {
                            if s_labels.len() >= num_labels {
                                num_labels = s_labels.len();
                                typ = Some(candidate.typ);
                                let s_len = if label.starts_with("!") {
                                    s_labels.len()-1
                                } else {
                                    s_labels.len()
                                };
                                suffix = Some(Self::assemble(input, s_len));
                                if d_labels.len() > s_len {
                                    registrable = Some(Self::assemble(input, s_len+1));
                                } else {
                                    registrable = None;
                                }
                            }
                        }
                    } else {
                        break;
                    }
                }
            }

            if suffix.is_none() && d_labels.len() > 1 && no_possible_matches_found {
                    suffix = Some(Self::assemble(input, 1));
                    registrable = Some(Self::assemble(input, 2));
            }
        }

        Ok(Domain {
            full: input.to_string(),
            typ: typ,
            suffix: suffix,
            registrable: registrable,
        })
    }

    pub fn parse(domain: &str, list: &List) -> Result<Domain> {
        let input = domain;

        let domain = input.trim().trim_right_matches('.');
        let (domain, res) = domain_to_unicode(domain);
        if let Err(errors) = res {
            return Err(ErrorKind::Uts46(errors).into());
        }

        let domain_is_valid = Domain::is_valid(input);

        let res = if domain_is_valid {
            Self::find_possible_matches(&domain, list)?
        } else {
            Vec::new()
        };

        Self::find_match(input, &domain, domain_is_valid, res)
    }

    pub fn root_domain(&self) -> Option<&String> {
        match self.registrable {
            Some(ref registrable) => Some(registrable),
            None => None,
        }
    }

    pub fn suffix(&self) -> Option<&String> {
        match self.suffix {
            Some(ref suffix) => Some(suffix),
            None => None,
        }
    }

    pub fn is_private(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => false,
                Type::Private => true,
            },
            None => false,
        }
    }

    pub fn is_icann(&self) -> bool {
        match self.typ {
            Some(typ) => match typ {
                Type::Icann => true,
                Type::Private => false,
            },
            None => false,
        }
    }
}
<|endoftext|>


<commit_before>

pub mod graph {
    use std::collections::HashMap;
    use std::ops::Deref;
    use std::mem;

    #[derive(Debug)]
    pub struct Graph {
        elements: i64,
        // we transmute our Boxed vertex into a pointer later
        vertices: HashMap<i64, *mut Vertex>,
    }

    impl Graph {
        pub fn new() -> Box<Graph> {
            let vertices: HashMap<i64, *mut Vertex> = HashMap::new();
            Box::new(Graph{elements:0, vertices:vertices})
        }

        pub fn add_vertex(&mut self) -> VertexProxy {
            let new_id = self.elements + 1;
            self.elements += 1;

            let mut v = Vertex::new(new_id);

            let ptr: *mut Vertex = unsafe { mem::transmute(v) };

            self.vertices.insert(new_id, ptr);

            // return the proxy which knows it's own id
            VertexProxy{id:new_id, v: ptr}
        }
        pub fn get(vertex_id:i64)  {
        }
    }

    /*
    * storing in & out edges seperately should cut down on the number of "things" i have to traverse
    * if a vertex has 10k edges (5k in and out 5k out) then doing something like
    *   g(v).outV()
    * should be ok
    */
    #[derive(Debug)]
    pub struct Vertex {
        pub id: i64,

        // pointers on both sides, yay
        pub out_edges: Vec<*mut Edge>,
        pub in_edges:  Vec<*mut Edge>,
    }

    impl Vertex {
        pub fn new(id: i64) -> Box<Vertex> {
            Box::new(Vertex{id:id,
                            out_edges: Vec::new(),
                            in_edges: Vec::new()})
        }
    }

    pub struct VertexProxy {
        pub id: i64,
        pub v: *mut Vertex,
    }

    impl VertexProxy {
        pub fn add_edge(&mut self, to_vertex: &mut VertexProxy) -> EdgeProxy {
            let in_vertex: &mut Vertex;
            let out_vertex: &mut Vertex;

            unsafe {
                in_vertex =  &mut *(self.v);
                out_vertex = &mut *(to_vertex.v);
            }

            // create the edge
            let mut e = Box::new(Edge{from_vertex: self.v, to_vertex: to_vertex.v});

            let mut edge: *mut Edge = unsafe { mem::transmute(e) };

            in_vertex.out_edges.push(edge);
            out_vertex.in_edges.push(edge);
            EdgeProxy{edge:edge}
        }

        pub fn query(self) {

        }
    }

    impl Deref for VertexProxy {
        type Target = Vertex;

        fn deref<'a>(&'a self) -> &'a Vertex {
            unsafe {
                &*(self.v)
            }
        }
    }

    #[derive(Debug)]
    pub struct Edge {
        from_vertex: *const Vertex,
        to_vertex: *const Vertex
    }

    pub struct EdgeProxy {
        edge: *mut Edge
    }

    impl Deref for EdgeProxy {
        type Target = Edge;
        fn deref<'a>(&'a self) -> &'a Edge {
            unsafe { &*(self.edge) }
        }
    }

}

mod tests {
    use graph::{Graph, Vertex};

    #[test]
    fn test_unsafe_vertex() {
        let v = Vertex::new(1);

        let out_edges = &v.out_edges;
        let in_edges = &v.in_edges;
    }

    #[test]
    fn test_deref() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();
        let vertex = &*v1;
        assert!(vertex.id == 1);
    }

    #[test]
    fn test_new_vertex() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();

        assert_eq!(v1.id, 1);

        unsafe {
            let vertex = &*v1;
            assert_eq!((*v1).id, 1);
        }

        let v2 = g.add_vertex();
        assert!(v2.id == 2);
    }

    #[test]
    fn test_add_edge() {
        let mut g = Graph::new();
        let mut v1 = g.add_vertex();
        let mut v2 = g.add_vertex();

        let e1 = v1.add_edge(&mut v2);
        v1.add_edge(&mut v2);
        v1.add_edge(&mut v2);

        assert_eq!(v1.out_edges.len(), v2.in_edges.len());

        // ensure the vertex and edge pointers are correct

        //assert_eq!(v1.v, *(e1.edge).from_vertex);
    }
}
<commit_msg>checking edge pointers<commit_after>

pub mod graph {
    use std::collections::HashMap;
    use std::ops::Deref;
    use std::mem;

    #[derive(Debug)]
    pub struct Graph {
        elements: i64,
        // we transmute our Boxed vertex into a pointer later
        vertices: HashMap<i64, *mut Vertex>,
    }

    impl Graph {
        pub fn new() -> Box<Graph> {
            let vertices: HashMap<i64, *mut Vertex> = HashMap::new();
            Box::new(Graph{elements:0, vertices:vertices})
        }

        pub fn add_vertex(&mut self) -> VertexProxy {
            let new_id = self.elements + 1;
            self.elements += 1;

            let mut v = Vertex::new(new_id);

            let ptr: *mut Vertex = unsafe { mem::transmute(v) };

            self.vertices.insert(new_id, ptr);

            // return the proxy which knows it's own id
            VertexProxy{id:new_id, v: ptr}
        }
        pub fn get(vertex_id:i64)  {
        }
    }

    /*
    * storing in & out edges seperately should cut down on the number of "things" i have to traverse
    * if a vertex has 10k edges (5k in and out 5k out) then doing something like
    *   g(v).outV()
    * should be ok
    */
    #[derive(Debug)]
    pub struct Vertex {
        pub id: i64,

        // pointers on both sides, yay
        pub out_edges: Vec<*mut Edge>,
        pub in_edges:  Vec<*mut Edge>,
    }

    impl Vertex {
        pub fn new(id: i64) -> Box<Vertex> {
            Box::new(Vertex{id:id,
                            out_edges: Vec::new(),
                            in_edges: Vec::new()})
        }
    }

    pub struct VertexProxy {
        pub id: i64,
        pub v: *mut Vertex,
    }

    impl VertexProxy {
        pub fn add_edge(&mut self, to_vertex: &mut VertexProxy) -> EdgeProxy {
            let in_vertex: &mut Vertex;
            let out_vertex: &mut Vertex;

            unsafe {
                in_vertex =  &mut *(self.v);
                out_vertex = &mut *(to_vertex.v);
            }

            // create the edge
            let mut e = Box::new(Edge{from_vertex: self.v, to_vertex: to_vertex.v});

            let mut edge: *mut Edge = unsafe { mem::transmute(e) };

            in_vertex.out_edges.push(edge);
            out_vertex.in_edges.push(edge);
            EdgeProxy{edge:edge}
        }

        pub fn query(self) {

        }
    }

    impl Deref for VertexProxy {
        type Target = Vertex;

        fn deref<'a>(&'a self) -> &'a Vertex {
            unsafe {
                &*(self.v)
            }
        }
    }

    #[derive(Debug)]
    pub struct Edge {
        pub from_vertex: *mut Vertex,
        pub to_vertex: *mut Vertex
    }

    pub struct EdgeProxy {
        edge: *mut Edge
    }

    impl Deref for EdgeProxy {
        type Target = Edge;
        fn deref<'a>(&'a self) -> &'a Edge {
            unsafe { &*(self.edge) }
        }
    }

}

mod tests {
    use graph::{Graph, Vertex};
    use std::mem;

    #[test]
    fn test_unsafe_vertex() {
        let v = Vertex::new(1);

        let out_edges = &v.out_edges;
        let in_edges = &v.in_edges;
    }

    #[test]
    fn test_deref() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();
        let vertex = &*v1;
        assert!(vertex.id == 1);
    }

    #[test]
    fn test_new_vertex() {
        let mut g = Graph::new();
        let v1 = g.add_vertex();

        assert_eq!(v1.id, 1);

        unsafe {
            let vertex = &*v1;
            assert_eq!((*v1).id, 1);
        }

        let v2 = g.add_vertex();
        assert!(v2.id == 2);
    }

    #[test]
    fn test_add_edge() {
        let mut g = Graph::new();
        let mut v1 = g.add_vertex();
        let mut v2 = g.add_vertex();

        let e1 = v1.add_edge(&mut v2);
        v1.add_edge(&mut v2);
        v1.add_edge(&mut v2);

        assert_eq!(v1.out_edges.len(), v2.in_edges.len());

        // ensure the vertex and edge pointers are correct

        assert_eq!(v1.v, e1.from_vertex);
        assert_eq!(e1.to_vertex, v2.v);

        //assert_eq!(v1.v, *(e1.edge).from_vertex);
    }
}
<|endoftext|>


<commit_before>
// Copyright 2014 Arjan Topolovec
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Library for reading and writing of tabular data.
//!
//! Supported formats:
//!
//! * DSV (Delimiter-separated values):
//!   Most common are CSV (comma-separated values) and TSV (tab-separated values) formats.
//!
//! * Fixed-width columns:
//!   Format where columns are of predefined fixed width, unused width is padded.
//!
//! # Reading is lazy
//!
//! All reading is done on-demand, no reading is done until request for the fist row comes.
//! Rows are read one at a time when requested.
//!
//! # Iteration flexibility
//!
//! Since rows are read as a lazy Iterator you have the flexibility of the Iterator api to control the iteration and row transformation.
//!
//! # Encoder/Decoder api
//!
//! There is no support for Encoder and Decoder api currently (or similar) but library is designed for such extension in the future.
//! Currently best way to achieve similar functionality is mapping a custom decoder over the Rows iterator.
#![crate_id = "tabular"]
#![license = "MIT/ASL2"]
#![crate_type = "lib"]
#![deny(warnings)]

mod common;

pub mod dsv;
pub mod fixed;
<commit_msg>Removed deny_warnings (mozilla/rust#14450)<commit_after>// Copyright 2014 Arjan Topolovec
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Library for reading and writing of tabular data.
//!
//! Supported formats:
//!
//! * DSV (Delimiter-separated values):
//!   Most common are CSV (comma-separated values) and TSV (tab-separated values) formats.
//!
//! * Fixed-width columns:
//!   Format where columns are of predefined fixed width, unused width is padded.
//!
//! # Reading is lazy
//!
//! All reading is done on-demand, no reading is done until request for the fist row comes.
//! Rows are read one at a time when requested.
//!
//! # Iteration flexibility
//!
//! Since rows are read as a lazy Iterator you have the flexibility of the Iterator api to control the iteration and row transformation.
//!
//! # Encoder/Decoder api
//!
//! There is no support for Encoder and Decoder api currently (or similar) but library is designed for such extension in the future.
//! Currently best way to achieve similar functionality is mapping a custom decoder over the Rows iterator.
#![crate_id = "tabular"]
#![license = "MIT/ASL2"]
#![crate_type = "lib"]

mod common;

pub mod dsv;
pub mod fixed;
<|endoftext|>