<commit_before>use std::env;
use std::fs;
use std::io::{self, prelude::*};
use std::process;

enum Op {
    Dec,
    Inc,
    Prev,
    Next,
    Loop(Box<[Op]>),
    Print,
}
use Op::*;

struct Tape {
    pos: usize,
    tape: Vec<i32>,
}

impl Tape {
    fn new() -> Self {
        Default::default()
    }

    fn get(&self) -> i32 {
        // Safe: `self.pos` is already checked in `self.next()`
        unsafe { *self.tape.get_unchecked(self.pos) }
    }

    fn dec(&mut self) {
        // Safe: `self.pos` is already checked in `self.next()`
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) -= 1;
        }
    }

    fn inc(&mut self) {
        // Safe: `self.pos` is already checked in `self.next()`
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) += 1;
        }
    }

    fn prev(&mut self) {
        self.pos -= 1;
    }

    fn next(&mut self) {
        self.pos += 1;
        if self.pos >= self.tape.len() {
            self.tape.resize(self.pos << 1, 0);
        }
    }
}

impl Default for Tape {
    fn default() -> Self {
        Self {
            pos: 0,
            tape: vec![0],
        }
    }
}

struct Printer<'a> {
    output: io::StdoutLock<'a>,
    sum1: i32,
    sum2: i32,
    quiet: bool,
}

impl<'a> Printer<'a> {
    fn new(output: &'a io::Stdout, quiet: bool) -> Self {
        Self {
            output: output.lock(),
            sum1: 0,
            sum2: 0,
            quiet,
        }
    }

    fn print(&mut self, n: i32) {
        if self.quiet {
            self.sum1 = (self.sum1 + n) % 255;
            self.sum2 = (self.sum2 + self.sum1) % 255;
        } else {
            self.output.write_all(&[n as u8]).ok();
            self.output.flush().ok();
        }
    }

    fn get_checksum(&self) -> i32 {
        (self.sum2 << 8) | self.sum1
    }
}

fn run(program: &[Op], tape: &mut Tape, p: &mut Printer) {
    for op in program {
        match op {
            Dec => tape.dec(),
            Inc => tape.inc(),
            Prev => tape.prev(),
            Next => tape.next(),
            Loop(program) => {
                while tape.get() > 0 {
                    run(program, tape, p);
                }
            }
            Print => {
                p.print(tape.get());
            }
        }
    }
}

fn parse<I: Iterator<Item = char>>(it: &mut I) -> Box<[Op]> {
    let mut buf = vec![];
    while let Some(c) = it.next() {
        buf.push(match c {
            '-' => Dec,
            '+' => Inc,
            '<' => Prev,
            '>' => Next,
            '.' => Print,
            '[' => Loop(parse(it)),
            ']' => break,
            _ => continue,
        });
    }
    buf.into_boxed_slice()
}

struct Program {
    ops: Box<[Op]>,
}

impl Program {
    fn new(code: &str) -> Self {
        Self {
            ops: parse(&mut code.chars()),
        }
    }

    fn run(&self, p: &mut Printer) {
        let mut tape = Tape::new();
        run(&self.ops, &mut tape, p);
    }
}

fn notify(msg: &str) {
    if let Ok(mut stream) = std::net::TcpStream::connect("localhost:9001") {
        stream.write_all(msg.as_bytes()).ok();
    }
}

fn verify() {
    let s = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>
             ---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";

    let left = {
        let output = io::stdout();
        let mut p_left = Printer::new(&output, true);
        Program::new(s).run(&mut p_left);
        p_left.get_checksum()
    };

    let right = {
        let output = io::stdout();
        let mut p_right = Printer::new(&output, true);
        for c in "Hello World!\n".chars() {
            p_right.print(c as i32);
        }
        p_right.get_checksum()
    };

    if left != right {
        eprintln!("{} != {}", left, right);
        process::exit(-1);
    }
}

fn main() {
    verify();

    let s = {
        let arg1 = env::args().nth(1).unwrap();
        fs::read_to_string(arg1).unwrap()
    };
    let output = io::stdout();
    let mut p = Printer::new(&output, env::var("QUIET").is_ok());

    notify(&format!("Rust\t{}", process::id()));
    Program::new(&s).run(&mut p);
    notify("stop");

    if p.quiet {
        println!("Output checksum: {}", p.get_checksum());
    }
}
<commit_msg>Rust: parse bytes like the C++ version and use `impl Trait` syntax (#363)<commit_after>use std::env;
use std::fs;
use std::io::{self, prelude::*};
use std::process;

enum Op {
    Dec,
    Inc,
    Prev,
    Next,
    Loop(Box<[Op]>),
    Print,
}
use Op::*;

struct Tape {
    pos: usize,
    tape: Vec<i32>,
}

impl Tape {
    fn new() -> Self {
        Default::default()
    }

    fn get(&self) -> i32 {
        // SAFETY: `self.pos` is already checked in `self.next()`
        unsafe { *self.tape.get_unchecked(self.pos) }
    }

    fn dec(&mut self) {
        // SAFETY: `self.pos` is already checked in `self.next()`
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) -= 1;
        }
    }

    fn inc(&mut self) {
        // SAFETY: `self.pos` is already checked in `self.next()`
        unsafe {
            *self.tape.get_unchecked_mut(self.pos) += 1;
        }
    }

    fn prev(&mut self) {
        self.pos -= 1;
    }

    fn next(&mut self) {
        self.pos += 1;
        if self.pos >= self.tape.len() {
            self.tape.resize(self.pos << 1, 0);
        }
    }
}

impl Default for Tape {
    fn default() -> Self {
        Self {
            pos: 0,
            tape: vec![0],
        }
    }
}

struct Printer<'a> {
    output: io::StdoutLock<'a>,
    sum1: i32,
    sum2: i32,
    quiet: bool,
}

impl<'a> Printer<'a> {
    fn new(output: &'a io::Stdout, quiet: bool) -> Self {
        Self {
            output: output.lock(),
            sum1: 0,
            sum2: 0,
            quiet,
        }
    }

    fn print(&mut self, n: i32) {
        if self.quiet {
            self.sum1 = (self.sum1 + n) % 255;
            self.sum2 = (self.sum2 + self.sum1) % 255;
        } else {
            self.output.write_all(&[n as u8]).ok();
            self.output.flush().ok();
        }
    }

    const fn get_checksum(&self) -> i32 {
        (self.sum2 << 8) | self.sum1
    }
}

fn run(program: &[Op], tape: &mut Tape, p: &mut Printer) {
    for op in program {
        match op {
            Dec => tape.dec(),
            Inc => tape.inc(),
            Prev => tape.prev(),
            Next => tape.next(),
            Loop(program) => {
                while tape.get() > 0 {
                    run(program, tape, p);
                }
            }
            Print => {
                p.print(tape.get());
            }
        }
    }
}

fn parse(it: &mut impl Iterator<Item = u8>) -> Box<[Op]> {
    let mut buf = vec![];
    while let Some(c) = it.next() {
        buf.push(match c {
            b'-' => Dec,
            b'+' => Inc,
            b'<' => Prev,
            b'>' => Next,
            b'.' => Print,
            b'[' => Loop(parse(it)),
            b']' => break,
            _ => continue,
        });
    }
    buf.into_boxed_slice()
}

struct Program {
    ops: Box<[Op]>,
}

impl Program {
    fn new(code: &[u8]) -> Self {
        Self {
            ops: parse(&mut code.iter().copied()),
        }
    }

    fn run(&self, p: &mut Printer) {
        let mut tape = Tape::new();
        run(&self.ops, &mut tape, p);
    }
}

fn notify(msg: &str) {
    if let Ok(mut stream) = std::net::TcpStream::connect("localhost:9001") {
        stream.write_all(msg.as_bytes()).ok();
    }
}

fn verify() {
    const S: &[u8] = b"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>\
                       ---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";

    let left = {
        let output = io::stdout();
        let mut p_left = Printer::new(&output, true);
        Program::new(S).run(&mut p_left);
        p_left.get_checksum()
    };

    let right = {
        let output = io::stdout();
        let mut p_right = Printer::new(&output, true);
        for &c in b"Hello World!\n" {
            p_right.print(c as i32);
        }
        p_right.get_checksum()
    };

    if left != right {
        eprintln!("{} != {}", left, right);
        process::exit(-1);
    }
}

fn main() {
    verify();

    let s = fs::read(env::args().nth(1).unwrap()).unwrap();
    let output = io::stdout();
    let mut p = Printer::new(&output, env::var("QUIET").is_ok());

    notify(&format!("Rust\t{}", process::id()));
    Program::new(&s).run(&mut p);
    notify("stop");

    if p.quiet {
        println!("Output checksum: {}", p.get_checksum());
    }
}
<|endoftext|>


<commit_before>use events::publish;
use eyre::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::Path;
use strum::{Display, EnumString, EnumVariantNames};
use tracing::Event;
use validator::Validate;

/// Logging level
#[derive(
    Debug,
    Display,
    Clone,
    Copy,
    PartialEq,
    PartialOrd,
    JsonSchema,
    Deserialize,
    Serialize,
    EnumString,
    EnumVariantNames,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum LoggingLevel {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
    Never,
}

/// Create a `LoggingLevel` from a `tracing::Level`
impl From<&tracing::Level> for LoggingLevel {
    fn from(level: &tracing::Level) -> Self {
        match *level {
            tracing::Level::TRACE => Self::Trace,
            tracing::Level::DEBUG => Self::Debug,
            tracing::Level::INFO => Self::Info,
            tracing::Level::WARN => Self::Warn,
            tracing::Level::ERROR => Self::Error,
        }
    }
}

/// Logging format
#[derive(
    Debug, PartialEq, Clone, Copy, JsonSchema, Deserialize, Serialize, EnumString, EnumVariantNames,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum LoggingFormat {
    Simple,
    Detail,
    Json,
}

pub mod config {
    use super::*;
    use defaults::Defaults;
    use std::fs;
    use std::{env, path::PathBuf};

    /// Get the directory where logs are stored
    pub fn dir(ensure: bool) -> Result<PathBuf> {
        let config = crate::config::dir(false)?;
        let dir = match env::consts::OS {
            "macos" | "windows" => config.join("Logs"),
            _ => config.join("logs"),
        };
        if ensure {
            fs::create_dir_all(&dir)?;
        }
        Ok(dir)
    }

    /// Logging to standard error stream
    ///
    /// Configuration settings for log entries printed to stderr when using the CLI
    #[derive(
        Debug, Defaults, PartialEq, Clone, Copy, JsonSchema, Deserialize, Serialize, Validate,
    )]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingStdErrConfig {
        /// The maximum log level to emit
        #[def = "LoggingLevel::Info"]
        pub level: LoggingLevel,

        /// The format for the logs entries
        #[def = "LoggingFormat::Simple"]
        pub format: LoggingFormat,
    }

    /// Logging to desktop notifications
    ///
    /// Configuration settings for log entries shown to the user in the desktop
    #[derive(
        Debug, Defaults, PartialEq, Clone, Copy, JsonSchema, Deserialize, Serialize, Validate,
    )]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingDesktopConfig {
        /// The maximum log level to emit
        #[def = "LoggingLevel::Info"]
        pub level: LoggingLevel,
    }

    /// Logging to file
    ///
    /// Configuration settings for logs entries written to file
    #[derive(Debug, Defaults, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingFileConfig {
        /// The path of the log file
        #[def = "default_file_path()"]
        pub path: String,

        /// The maximum log level to emit
        #[def = "LoggingLevel::Info"]
        pub level: LoggingLevel,
    }

    /// Get the default value for `logging.file.path`
    pub fn default_file_path() -> String {
        dir(true)
            .expect("Unable to get logs directory")
            .join("log.json")
            .into_os_string()
            .into_string()
            .expect("Unable to convert path to string")
    }

    /// Logging
    ///
    /// Configuration settings for logging
    #[derive(Debug, Default, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingConfig {
        pub stderr: LoggingStdErrConfig,
        pub desktop: LoggingDesktopConfig,
        pub file: LoggingFileConfig,
    }
}

/// Create a preliminary logging subscriber.
///
/// This can be necessary to ensure that any log events that get emitted during
/// initialization are displayed to the user.
pub fn prelim() -> tracing::subscriber::DefaultGuard {
    let subscriber = tracing_subscriber::fmt()
        .pretty()
        .with_max_level(tracing::Level::INFO)
        .with_writer(std::io::stderr)
        .finish();
    tracing::subscriber::set_default(subscriber)
}

/// Custom tracing_subscriber layer that prints events to stderr filtered
/// by level for the "plain" format. Other formats are handled by `tracing_subscriber`
/// formatters (see below).
struct StderrPlainLayer {
    level: LoggingLevel,
}

#[derive(Default)]
struct StderrVisitor {
    message: String,
}

impl tracing::field::Visit for StderrVisitor {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        if field.name() == "message" {
            self.message = format!("{:?}", value);
        }
    }
}

impl<S: tracing::subscriber::Subscriber> tracing_subscriber::layer::Layer<S> for StderrPlainLayer {
    fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
        let level = LoggingLevel::from(event.metadata().level());
        if level >= self.level {
            let level_name = level.to_string().to_uppercase();

            #[cfg(feature = "cli-pretty")]
            let level_name = {
                use cli_utils::ansi_term::Color::{Blue, Green, Purple, Red, White, Yellow};
                match level {
                    LoggingLevel::Trace => Purple,
                    LoggingLevel::Debug => Blue,
                    LoggingLevel::Info => Green,
                    LoggingLevel::Warn => Yellow,
                    LoggingLevel::Error => Red,
                    _ => White,
                }
                .bold()
                .paint(format!("{:5}", level_name))
            };

            let mut visitor = StderrVisitor::default();
            event.record(&mut visitor);
            eprintln!("{} {}", level_name, visitor.message)
        }
    }
}

/// Custom tracing_subscriber layer that publishes events
/// under the pubsub "logging" topic as a JSON value.
struct PubSubLayer {
    level: LoggingLevel,
}

impl<S: tracing::subscriber::Subscriber> tracing_subscriber::layer::Layer<S> for PubSubLayer {
    fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
        use tracing_serde::AsSerde;
        if LoggingLevel::from(event.metadata().level()) >= self.level {
            publish("logging", &event.as_serde())
        }
    }
}

/// Initialize logging
///
/// This initializes a logging subscriber based on configuration and
/// context (e.g. stderr should not be written to if the context
/// is the desktop application).
///
/// # Arguments
///
/// - `stderr`: should stderr logging be enabled
/// - `pubsub`: should pubsub logging be enabled (for desktop notifications)
/// - `file`: should file logging be enabled
/// - `config`: the logging configuration
pub fn init(
    stderr: bool,
    pubsub: bool,
    file: bool,
    config: &config::LoggingConfig,
) -> Result<tracing_appender::non_blocking::WorkerGuard> {
    use tracing_error::ErrorLayer;
    use tracing_subscriber::prelude::*;
    use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter};

    // Stderr logging layer
    let stderr_level = if stderr {
        config.stderr.level
    } else {
        LoggingLevel::Never
    };

    // Pubsub logging layer (used for desktop notifications)
    let pubsub_level = if pubsub {
        config.desktop.level
    } else {
        LoggingLevel::Never
    };
    let pubsub_layer = PubSubLayer {
        level: pubsub_level,
    };

    // File logging layer
    let file_level = if file {
        config.file.level
    } else {
        LoggingLevel::Never
    };
    let (file_writer, file_guard) = if file_level != LoggingLevel::Never {
        let path = Path::new(&config.file.path);
        let file_appender =
            tracing_appender::rolling::daily(&path.parent().unwrap(), &path.file_name().unwrap());
        tracing_appender::non_blocking(file_appender)
    } else {
        tracing_appender::non_blocking(std::io::sink())
    };
    let file_layer = fmt::Layer::new().json().with_writer(file_writer);

    // Error reporting layer (necessary for using `eyre` crate)
    let error_layer = ErrorLayer::default();

    // tracing_subscriber does not currently allow for different layers to have different
    // levels so work out the minimum level and filter by that in the root subscriber.
    let mut min_level = LoggingLevel::Never;
    if stderr_level < min_level {
        min_level = stderr_level
    }
    if pubsub_level < min_level {
        min_level = pubsub_level
    }
    if file_level < min_level {
        min_level = file_level
    }

    // Filter out debug log entries from some crates to avoid excessive noise.
    // We may want to show entries from other crates during development
    // so we may add another flag for this in the future.
    // e.g. `--log-scope=stencila` vs `--log-scope=all`.
    let directives = format!(
        "{},html5ever=info,hyper=info,mio=info,reqwest=info,rustyline=info,tungstenite=info,warp=info",
        min_level.to_string()
    );

    let registry = tracing_subscriber::registry()
        .with(EnvFilter::new(directives))
        .with(pubsub_layer)
        .with(file_layer)
        .with(error_layer);

    if config.stderr.format == LoggingFormat::Detail {
        registry.with(fmt::Layer::new().pretty()).init();
    } else if config.stderr.format == LoggingFormat::Json {
        registry.with(fmt::Layer::new().json()).init();
    } else {
        registry
            .with(StderrPlainLayer {
                level: stderr_level,
            })
            .init();
    }

    Ok(file_guard)
}

/// Generate some test tracing events.
///
/// Can be used for testing that events are propagated
/// to subscribers as expected.
#[tracing::instrument]
pub fn test_events() {
    tracing::trace!("A trace event");
    tracing::debug!("A debug event");
    tracing::info!("An info event");
    tracing::warn!("A warn event");
    tracing::error!("An error event");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[allow(clippy::eq_op)]
    fn test_logging_level_ordering() -> Result<()> {
        assert!(LoggingLevel::Debug > LoggingLevel::Trace);
        assert!(LoggingLevel::Debug >= LoggingLevel::Debug);
        assert!(LoggingLevel::Debug == LoggingLevel::Debug);
        assert!(LoggingLevel::Trace <= LoggingLevel::Debug);
        assert!(LoggingLevel::Trace < LoggingLevel::Debug);

        assert!(LoggingLevel::Info > LoggingLevel::Debug);
        assert!(LoggingLevel::Warn > LoggingLevel::Info);
        assert!(LoggingLevel::Error > LoggingLevel::Warn);
        Ok(())
    }
}
<commit_msg>chore(Logging): Add another exclusion<commit_after>use events::publish;
use eyre::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::Path;
use strum::{Display, EnumString, EnumVariantNames};
use tracing::Event;
use validator::Validate;

/// Logging level
#[derive(
    Debug,
    Display,
    Clone,
    Copy,
    PartialEq,
    PartialOrd,
    JsonSchema,
    Deserialize,
    Serialize,
    EnumString,
    EnumVariantNames,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum LoggingLevel {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
    Never,
}

/// Create a `LoggingLevel` from a `tracing::Level`
impl From<&tracing::Level> for LoggingLevel {
    fn from(level: &tracing::Level) -> Self {
        match *level {
            tracing::Level::TRACE => Self::Trace,
            tracing::Level::DEBUG => Self::Debug,
            tracing::Level::INFO => Self::Info,
            tracing::Level::WARN => Self::Warn,
            tracing::Level::ERROR => Self::Error,
        }
    }
}

/// Logging format
#[derive(
    Debug, PartialEq, Clone, Copy, JsonSchema, Deserialize, Serialize, EnumString, EnumVariantNames,
)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum LoggingFormat {
    Simple,
    Detail,
    Json,
}

pub mod config {
    use super::*;
    use defaults::Defaults;
    use std::fs;
    use std::{env, path::PathBuf};

    /// Get the directory where logs are stored
    pub fn dir(ensure: bool) -> Result<PathBuf> {
        let config = crate::config::dir(false)?;
        let dir = match env::consts::OS {
            "macos" | "windows" => config.join("Logs"),
            _ => config.join("logs"),
        };
        if ensure {
            fs::create_dir_all(&dir)?;
        }
        Ok(dir)
    }

    /// Logging to standard error stream
    ///
    /// Configuration settings for log entries printed to stderr when using the CLI
    #[derive(
        Debug, Defaults, PartialEq, Clone, Copy, JsonSchema, Deserialize, Serialize, Validate,
    )]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingStdErrConfig {
        /// The maximum log level to emit
        #[def = "LoggingLevel::Info"]
        pub level: LoggingLevel,

        /// The format for the logs entries
        #[def = "LoggingFormat::Simple"]
        pub format: LoggingFormat,
    }

    /// Logging to desktop notifications
    ///
    /// Configuration settings for log entries shown to the user in the desktop
    #[derive(
        Debug, Defaults, PartialEq, Clone, Copy, JsonSchema, Deserialize, Serialize, Validate,
    )]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingDesktopConfig {
        /// The maximum log level to emit
        #[def = "LoggingLevel::Info"]
        pub level: LoggingLevel,
    }

    /// Logging to file
    ///
    /// Configuration settings for logs entries written to file
    #[derive(Debug, Defaults, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingFileConfig {
        /// The path of the log file
        #[def = "default_file_path()"]
        pub path: String,

        /// The maximum log level to emit
        #[def = "LoggingLevel::Info"]
        pub level: LoggingLevel,
    }

    /// Get the default value for `logging.file.path`
    pub fn default_file_path() -> String {
        dir(true)
            .expect("Unable to get logs directory")
            .join("log.json")
            .into_os_string()
            .into_string()
            .expect("Unable to convert path to string")
    }

    /// Logging
    ///
    /// Configuration settings for logging
    #[derive(Debug, Default, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default)]
    #[schemars(deny_unknown_fields)]
    pub struct LoggingConfig {
        pub stderr: LoggingStdErrConfig,
        pub desktop: LoggingDesktopConfig,
        pub file: LoggingFileConfig,
    }
}

/// Create a preliminary logging subscriber.
///
/// This can be necessary to ensure that any log events that get emitted during
/// initialization are displayed to the user.
pub fn prelim() -> tracing::subscriber::DefaultGuard {
    let subscriber = tracing_subscriber::fmt()
        .pretty()
        .with_max_level(tracing::Level::INFO)
        .with_writer(std::io::stderr)
        .finish();
    tracing::subscriber::set_default(subscriber)
}

/// Custom tracing_subscriber layer that prints events to stderr filtered
/// by level for the "plain" format. Other formats are handled by `tracing_subscriber`
/// formatters (see below).
struct StderrPlainLayer {
    level: LoggingLevel,
}

#[derive(Default)]
struct StderrVisitor {
    message: String,
}

impl tracing::field::Visit for StderrVisitor {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        if field.name() == "message" {
            self.message = format!("{:?}", value);
        }
    }
}

impl<S: tracing::subscriber::Subscriber> tracing_subscriber::layer::Layer<S> for StderrPlainLayer {
    fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
        let level = LoggingLevel::from(event.metadata().level());
        if level >= self.level {
            let level_name = level.to_string().to_uppercase();

            #[cfg(feature = "cli-pretty")]
            let level_name = {
                use cli_utils::ansi_term::Color::{Blue, Green, Purple, Red, White, Yellow};
                match level {
                    LoggingLevel::Trace => Purple,
                    LoggingLevel::Debug => Blue,
                    LoggingLevel::Info => Green,
                    LoggingLevel::Warn => Yellow,
                    LoggingLevel::Error => Red,
                    _ => White,
                }
                .bold()
                .paint(format!("{:5}", level_name))
            };

            let mut visitor = StderrVisitor::default();
            event.record(&mut visitor);
            eprintln!("{} {}", level_name, visitor.message)
        }
    }
}

/// Custom tracing_subscriber layer that publishes events
/// under the pubsub "logging" topic as a JSON value.
struct PubSubLayer {
    level: LoggingLevel,
}

impl<S: tracing::subscriber::Subscriber> tracing_subscriber::layer::Layer<S> for PubSubLayer {
    fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
        use tracing_serde::AsSerde;
        if LoggingLevel::from(event.metadata().level()) >= self.level {
            publish("logging", &event.as_serde())
        }
    }
}

/// Initialize logging
///
/// This initializes a logging subscriber based on configuration and
/// context (e.g. stderr should not be written to if the context
/// is the desktop application).
///
/// # Arguments
///
/// - `stderr`: should stderr logging be enabled
/// - `pubsub`: should pubsub logging be enabled (for desktop notifications)
/// - `file`: should file logging be enabled
/// - `config`: the logging configuration
pub fn init(
    stderr: bool,
    pubsub: bool,
    file: bool,
    config: &config::LoggingConfig,
) -> Result<tracing_appender::non_blocking::WorkerGuard> {
    use tracing_error::ErrorLayer;
    use tracing_subscriber::prelude::*;
    use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter};

    // Stderr logging layer
    let stderr_level = if stderr {
        config.stderr.level
    } else {
        LoggingLevel::Never
    };

    // Pubsub logging layer (used for desktop notifications)
    let pubsub_level = if pubsub {
        config.desktop.level
    } else {
        LoggingLevel::Never
    };
    let pubsub_layer = PubSubLayer {
        level: pubsub_level,
    };

    // File logging layer
    let file_level = if file {
        config.file.level
    } else {
        LoggingLevel::Never
    };
    let (file_writer, file_guard) = if file_level != LoggingLevel::Never {
        let path = Path::new(&config.file.path);
        let file_appender =
            tracing_appender::rolling::daily(&path.parent().unwrap(), &path.file_name().unwrap());
        tracing_appender::non_blocking(file_appender)
    } else {
        tracing_appender::non_blocking(std::io::sink())
    };
    let file_layer = fmt::Layer::new().json().with_writer(file_writer);

    // Error reporting layer (necessary for using `eyre` crate)
    let error_layer = ErrorLayer::default();

    // tracing_subscriber does not currently allow for different layers to have different
    // levels so work out the minimum level and filter by that in the root subscriber.
    let mut min_level = LoggingLevel::Never;
    if stderr_level < min_level {
        min_level = stderr_level
    }
    if pubsub_level < min_level {
        min_level = pubsub_level
    }
    if file_level < min_level {
        min_level = file_level
    }

    // Filter out debug log entries from some crates to avoid excessive noise.
    // We may want to show entries from other crates during development
    // so we may add another flag for this in the future.
    // e.g. `--log-scope=stencila` vs `--log-scope=all`.
    let directives = format!(
        "{},html5ever=info,hyper=info,mio=info,reqwest=info,rustyline=info,tokio_tungstenite=info,tungstenite=info,warp=info",
        min_level.to_string()
    );

    let registry = tracing_subscriber::registry()
        .with(EnvFilter::new(directives))
        .with(pubsub_layer)
        .with(file_layer)
        .with(error_layer);

    if config.stderr.format == LoggingFormat::Detail {
        registry.with(fmt::Layer::new().pretty()).init();
    } else if config.stderr.format == LoggingFormat::Json {
        registry.with(fmt::Layer::new().json()).init();
    } else {
        registry
            .with(StderrPlainLayer {
                level: stderr_level,
            })
            .init();
    }

    Ok(file_guard)
}

/// Generate some test tracing events.
///
/// Can be used for testing that events are propagated
/// to subscribers as expected.
#[tracing::instrument]
pub fn test_events() {
    tracing::trace!("A trace event");
    tracing::debug!("A debug event");
    tracing::info!("An info event");
    tracing::warn!("A warn event");
    tracing::error!("An error event");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[allow(clippy::eq_op)]
    fn test_logging_level_ordering() -> Result<()> {
        assert!(LoggingLevel::Debug > LoggingLevel::Trace);
        assert!(LoggingLevel::Debug >= LoggingLevel::Debug);
        assert!(LoggingLevel::Debug == LoggingLevel::Debug);
        assert!(LoggingLevel::Trace <= LoggingLevel::Debug);
        assert!(LoggingLevel::Trace < LoggingLevel::Debug);

        assert!(LoggingLevel::Info > LoggingLevel::Debug);
        assert!(LoggingLevel::Warn > LoggingLevel::Info);
        assert!(LoggingLevel::Error > LoggingLevel::Warn);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ptr;
use std::sync::{
    Arc,
    Mutex,
};
use std::thread;
use std::time::Duration;

use winapi::winsock2::{
    fd_set,
    FD_SETSIZE,
    SOCKET_ERROR,
    timeval,
    WSADATA,
};
use ws2_32::{
    select,
    WSACleanup,
    WSAStartup,
};

use c_ares;
use mio;

use error::ResolverError;
use eventloop::EventLoopHandle;

// The EventLoop will use select() to check on the status of file descriptors
// that c-ares cares about.
//
// It also waits for a message telling it to shut down.  We use a mio channel
// here only for consistency with the unix interface.
pub struct EventLoop {
    tx_msg_channel: mio::channel::Sender<Message>,
    rx_msg_channel: mio::channel::Receiver<Message>,
    pub ares_channel: Arc<Mutex<c_ares::Channel>>,
    quit: bool,
}

// Messages for the event loop.
#[derive(Debug)]
pub enum Message {
    // 'Shut down'.
    ShutDown,
}

impl EventLoop {
    // Create a new event loop.
    pub fn new(options: c_ares::Options) -> Result<EventLoop, ResolverError> {
        // Initialize sockets.
        unsafe {
            let mut wsadata: WSADATA = mem::uninitialized();
            WSAStartup(0x101, &mut wsadata);
        }

        // Create the message channel.
        let (tx, rx) = mio::channel::channel();

        // Create the c-ares channel.
        let ares_channel = try!(c_ares::Channel::new(options));
        let locked_channel = Arc::new(Mutex::new(ares_channel));

        // Create and return the event loop.
        let event_loop = EventLoop {
            tx_msg_channel: tx,
            rx_msg_channel: rx,
            ares_channel: locked_channel,
            quit: false,
        };
        Ok(event_loop)
    }

    // Run the event loop.
    pub fn run(self) -> EventLoopHandle {
        let tx_clone = self.tx_msg_channel.clone();
        let join_handle = thread::spawn(|| self.event_loop_thread());
        EventLoopHandle::new(join_handle, tx_clone)
    }

    // Event loop thread - waits for events, and handles them.
    fn event_loop_thread(mut self) {
        let timeout = timeval {
            tv_sec: 0,
            tv_usec: 200000,
        };
        let duration = Duration::from_millis(200);

        // Loop round, asking c-ares what it cares about and doing as asked.
        loop {
            let mut read_fds = fd_set {
                fd_count: 0,
                fd_array: [c_ares::SOCKET_BAD; FD_SETSIZE],
            };
            let mut write_fds = fd_set {
                fd_count: 0,
                fd_array: [c_ares::SOCKET_BAD; FD_SETSIZE],
            };
            let count = self.ares_channel.lock().unwrap().fds(&mut read_fds, &mut write_fds);

            // Wait for something to happen.
            let results = unsafe {
                if count == 0 {
                    thread::sleep(duration);
                    0
                } else {
                    select(0, &mut read_fds, &mut write_fds, ptr::null_mut(), &timeout)
                }
            };

            // Process whatever happened.
            match results {
                SOCKET_ERROR => panic!("Socket error"),
                _ => self.ares_channel.lock().unwrap().process(&mut read_fds, &mut write_fds),
            }

            // Check whether we're asked to quit.
            self.handle_messages();
            if self.quit { break }
        }
    }

    // Process messages incoming on the channel.
    fn handle_messages(&mut self) {
        // The only possible message is an instruction to shut down.
        if let Ok(Message::ShutDown) = self.rx_msg_channel.try_recv() {
            self.quit = true;
        }
    }
}

impl Drop for EventLoop {
    fn drop(&mut self) {
        unsafe { WSACleanup(); }
    }
}
<commit_msg>long lines<commit_after>use std::mem;
use std::ptr;
use std::sync::{
    Arc,
    Mutex,
};
use std::thread;
use std::time::Duration;

use winapi::winsock2::{
    fd_set,
    FD_SETSIZE,
    SOCKET_ERROR,
    timeval,
    WSADATA,
};
use ws2_32::{
    select,
    WSACleanup,
    WSAStartup,
};

use c_ares;
use mio;

use error::ResolverError;
use eventloop::EventLoopHandle;

// The EventLoop will use select() to check on the status of file descriptors
// that c-ares cares about.
//
// It also waits for a message telling it to shut down.  We use a mio channel
// here only for consistency with the unix interface.
pub struct EventLoop {
    tx_msg_channel: mio::channel::Sender<Message>,
    rx_msg_channel: mio::channel::Receiver<Message>,
    pub ares_channel: Arc<Mutex<c_ares::Channel>>,
    quit: bool,
}

// Messages for the event loop.
#[derive(Debug)]
pub enum Message {
    // 'Shut down'.
    ShutDown,
}

impl EventLoop {
    // Create a new event loop.
    pub fn new(options: c_ares::Options) -> Result<EventLoop, ResolverError> {
        // Initialize sockets.
        unsafe {
            let mut wsadata: WSADATA = mem::uninitialized();
            WSAStartup(0x101, &mut wsadata);
        }

        // Create the message channel.
        let (tx, rx) = mio::channel::channel();

        // Create the c-ares channel.
        let ares_channel = try!(c_ares::Channel::new(options));
        let locked_channel = Arc::new(Mutex::new(ares_channel));

        // Create and return the event loop.
        let event_loop = EventLoop {
            tx_msg_channel: tx,
            rx_msg_channel: rx,
            ares_channel: locked_channel,
            quit: false,
        };
        Ok(event_loop)
    }

    // Run the event loop.
    pub fn run(self) -> EventLoopHandle {
        let tx_clone = self.tx_msg_channel.clone();
        let join_handle = thread::spawn(|| self.event_loop_thread());
        EventLoopHandle::new(join_handle, tx_clone)
    }

    // Event loop thread - waits for events, and handles them.
    fn event_loop_thread(mut self) {
        let timeout = timeval {
            tv_sec: 0,
            tv_usec: 200000,
        };
        let duration = Duration::from_millis(200);

        // Loop round, asking c-ares what it cares about and doing as asked.
        loop {
            let mut read_fds = fd_set {
                fd_count: 0,
                fd_array: [c_ares::SOCKET_BAD; FD_SETSIZE],
            };
            let mut write_fds = fd_set {
                fd_count: 0,
                fd_array: [c_ares::SOCKET_BAD; FD_SETSIZE],
            };
            let count = self.ares_channel.lock().unwrap()
                .fds(&mut read_fds, &mut write_fds);

            // Wait for something to happen.
            let results = unsafe {
                if count == 0 {
                    thread::sleep(duration);
                    0
                } else {
                    select(
                        0,
                        &mut read_fds,
                        &mut write_fds,
                        ptr::null_mut(),
                        &timeout
                    )
                }
            };

            // Process whatever happened.
            match results {
                SOCKET_ERROR => panic!("Socket error"),
                _ => self.ares_channel.lock().unwrap()
                    .process(&mut read_fds, &mut write_fds),
            }

            // Check whether we're asked to quit.
            self.handle_messages();
            if self.quit { break }
        }
    }

    // Process messages incoming on the channel.
    fn handle_messages(&mut self) {
        // The only possible message is an instruction to shut down.
        if let Ok(Message::ShutDown) = self.rx_msg_channel.try_recv() {
            self.quit = true;
        }
    }
}

impl Drop for EventLoop {
    fn drop(&mut self) {
        unsafe { WSACleanup(); }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 Torrie Fischer <tdfischer@hackerbots.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::str::FromStr;

#[derive(Debug,PartialEq,Eq)]
pub enum Command {
    Nick,
    User,
    Join,
    Part,
    Quit,
    Ping,
    Mode,
    Pong,
    Pass,
    Privmsg,
    Topic,
    Numeric(u32),
    Unknown(String)
}

impl Command {
    pub fn as_string(&self) -> String {
        match self {
            &Command::Nick => "NICK".to_string(),
            &Command::Join => "JOIN".to_string(),
            &Command::Part => "PART".to_string(),
            &Command::Pong => "PONG".to_string(),
            &Command::Privmsg => "PRIVMSG".to_string(),
            &Command::User => "USER".to_string(),
            &Command::Quit => "QUIT".to_string(),
            &Command::Ping => "PING".to_string(),
            &Command::Mode => "MODE".to_string(),
            &Command::Pass => "PASS".to_string(),
            &Command::Topic => "TOPIC".to_string(),
            &Command::Numeric(n)=> format!("{:0>3}", n),
            &Command::Unknown(ref s) => s.clone()
        }
    }
}

impl Message {
    pub fn to_string(&self) -> String {
        let mut ret = String::new();
        match self.prefix {
            Some(ref pfx) => {
                ret.push(':');
                ret.push_str(pfx.trim());
                ret.push(' ');
            },
            None => ()
        };
        ret.push_str(self.command.as_string().trim());
        for ref arg in self.args.iter() {
            ret.push(' ');
            ret.push_str(arg.trim());
        }

        match self.suffix {
            Some(ref sfx) => {
                ret.push_str(" :");
                ret.push_str(sfx.trim());
            },
            None => ()
        };

        return ret;
    }

    fn split_parts(line: &str) -> (Option<String>, &str, Option<String>) {
        let mut prefix_end = 0;
        if line.starts_with(":") {
            for c in line[1..].chars() {
                prefix_end += 1;
                if c == ' ' {
                    break
                }
            }
        }

        let mut args_end = prefix_end;
        let mut found_space = false;
        for c in line[prefix_end..].chars() {
            if c == ' ' {
                found_space = true;
            } else if c == ':' && found_space {
                args_end -= 1;
                break
            } else {
                found_space = false;
            }
            args_end += 1;
        }

        let prefix = match prefix_end {
            0 => None,
            _ => Some(line[1..prefix_end].to_string())
        };
        let args = &line[prefix_end..args_end].trim();
        let len = line.len();
        let suffix = if args_end == len {
            None
        } else {
            Some(line[args_end+2..].to_string())
        };

        (prefix, args, suffix)
    }

    pub fn from_str(line: &str) -> Self {
        let parts = Self::split_parts(line.trim());
        let split: Vec<&str> = parts.1.split(" ").collect();
        let mut args = Vec::new();
        for s in split[1..].iter() {
            args.push(s.to_string());
        }
        let parsed_command: Result<Command, Command> = split[0].parse();
        Message{
            prefix: parts.0,
            command: parsed_command.ok().unwrap(),
            args: args,
            suffix: parts.2
        }
    }
}

impl From<Command> for Message {
    fn from(c: Command) -> Message {
        Message {
            prefix: None,
            command: c,
            args: vec![],
            suffix: None
        }
    }
}

impl FromStr for Command {
    type Err = Command;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NICK" => Ok(Command::Nick),
            "USER" => Ok(Command::User),
            "JOIN" => Ok(Command::Join),
            "PART" => Ok(Command::Part),
            "QUIT" => Ok(Command::Quit),
            "PING" => Ok(Command::Ping),
            "MODE" => Ok(Command::Mode),
            "PASS" => Ok(Command::Pass),
            "TOPIC" => Ok(Command::Topic),
            "PRIVMSG" => Ok(Command::Privmsg),
            _ => Ok(Command::Unknown(s.to_string()))
        }
    }
}

#[derive(Debug)]
pub struct Message {
    pub prefix: Option<String>,
    pub command: Command,
    pub args: Vec<String>,
    pub suffix: Option<String>
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn classic_irc_session() {
        let msg = Message::from_str("USER nick 0 * hostname");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::User);
        assert_eq!(msg.args, &["nick", "0", "*", "hostname"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str("NICK nick");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::Nick);
        assert_eq!(msg.args, &["nick"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str(":nick!nick@hostname JOIN #foo");
        assert_eq!(msg.prefix, Some("nick!nick@hostname".to_owned()));
        assert_eq!(msg.command, Command::Join);
        assert_eq!(msg.args, &["#foo"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str(":nick!nick@hostname PRIVMSG #foo :Hello World!");
        assert_eq!(msg.prefix, Some("nick!nick@hostname".to_owned()));
        assert_eq!(msg.command, Command::Privmsg);
        assert_eq!(msg.args, &["#foo"]);
        assert_eq!(msg.suffix, Some("Hello World!".to_owned()));
    }

    #[test]
    fn weird_cases() {
        let msg = Message::from_str("USER nick:name 0 * hostname");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::User);
        assert_eq!(msg.args, &["nick:name", "0", "*", "hostname"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str("USER  nick  0  *  hostname");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::User);
        assert_eq!(msg.args, &["", "nick", "", "0", "", "*", "", "hostname"]);
        assert_eq!(msg.suffix, None);
    }
}<commit_msg>Add utf8 tests for IRC parser.<commit_after>/*
 * Copyright 2015-2016 Torrie Fischer <tdfischer@hackerbots.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::str::FromStr;

#[derive(Debug,PartialEq,Eq)]
pub enum Command {
    Nick,
    User,
    Join,
    Part,
    Quit,
    Ping,
    Mode,
    Pong,
    Pass,
    Privmsg,
    Topic,
    Numeric(u32),
    Unknown(String)
}

impl Command {
    pub fn as_string(&self) -> String {
        match self {
            &Command::Nick => "NICK".to_string(),
            &Command::Join => "JOIN".to_string(),
            &Command::Part => "PART".to_string(),
            &Command::Pong => "PONG".to_string(),
            &Command::Privmsg => "PRIVMSG".to_string(),
            &Command::User => "USER".to_string(),
            &Command::Quit => "QUIT".to_string(),
            &Command::Ping => "PING".to_string(),
            &Command::Mode => "MODE".to_string(),
            &Command::Pass => "PASS".to_string(),
            &Command::Topic => "TOPIC".to_string(),
            &Command::Numeric(n)=> format!("{:0>3}", n),
            &Command::Unknown(ref s) => s.clone()
        }
    }
}

impl Message {
    pub fn to_string(&self) -> String {
        let mut ret = String::new();
        match self.prefix {
            Some(ref pfx) => {
                ret.push(':');
                ret.push_str(pfx.trim());
                ret.push(' ');
            },
            None => ()
        };
        ret.push_str(self.command.as_string().trim());
        for ref arg in self.args.iter() {
            ret.push(' ');
            ret.push_str(arg.trim());
        }

        match self.suffix {
            Some(ref sfx) => {
                ret.push_str(" :");
                ret.push_str(sfx.trim());
            },
            None => ()
        };

        return ret;
    }

    fn split_parts(line: &str) -> (Option<String>, &str, Option<String>) {
        let mut prefix_end = 0;
        if line.starts_with(":") {
            for c in line[1..].chars() {
                prefix_end += 1;
                if c == ' ' {
                    break
                }
            }
        }

        let mut args_end = prefix_end;
        let mut found_space = false;
        for c in line[prefix_end..].chars() {
            if c == ' ' {
                found_space = true;
            } else if c == ':' && found_space {
                args_end -= 1;
                break
            } else {
                found_space = false;
            }
            args_end += 1;
        }

        let prefix = match prefix_end {
            0 => None,
            _ => Some(line[1..prefix_end].to_string())
        };
        let args = &line[prefix_end..args_end].trim();
        let len = line.len();
        let suffix = if args_end == len {
            None
        } else {
            Some(line[args_end+2..].to_string())
        };

        (prefix, args, suffix)
    }

    pub fn from_str(line: &str) -> Self {
        let parts = Self::split_parts(line.trim());
        let split: Vec<&str> = parts.1.split(" ").collect();
        let mut args = Vec::new();
        for s in split[1..].iter() {
            args.push(s.to_string());
        }
        let parsed_command: Result<Command, Command> = split[0].parse();
        Message{
            prefix: parts.0,
            command: parsed_command.ok().unwrap(),
            args: args,
            suffix: parts.2
        }
    }
}

impl From<Command> for Message {
    fn from(c: Command) -> Message {
        Message {
            prefix: None,
            command: c,
            args: vec![],
            suffix: None
        }
    }
}

impl FromStr for Command {
    type Err = Command;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NICK" => Ok(Command::Nick),
            "USER" => Ok(Command::User),
            "JOIN" => Ok(Command::Join),
            "PART" => Ok(Command::Part),
            "QUIT" => Ok(Command::Quit),
            "PING" => Ok(Command::Ping),
            "MODE" => Ok(Command::Mode),
            "PASS" => Ok(Command::Pass),
            "TOPIC" => Ok(Command::Topic),
            "PRIVMSG" => Ok(Command::Privmsg),
            _ => Ok(Command::Unknown(s.to_string()))
        }
    }
}

#[derive(Debug)]
pub struct Message {
    pub prefix: Option<String>,
    pub command: Command,
    pub args: Vec<String>,
    pub suffix: Option<String>
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn classic_irc_session() {
        let msg = Message::from_str("USER nick 0 * hostname");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::User);
        assert_eq!(msg.args, &["nick", "0", "*", "hostname"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str("NICK nick");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::Nick);
        assert_eq!(msg.args, &["nick"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str(":nick!nick@hostname JOIN #foo");
        assert_eq!(msg.prefix, Some("nick!nick@hostname".to_owned()));
        assert_eq!(msg.command, Command::Join);
        assert_eq!(msg.args, &["#foo"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str(":nick!nick@hostname PRIVMSG #foo :Hello World!");
        assert_eq!(msg.prefix, Some("nick!nick@hostname".to_owned()));
        assert_eq!(msg.command, Command::Privmsg);
        assert_eq!(msg.args, &["#foo"]);
        assert_eq!(msg.suffix, Some("Hello World!".to_owned()));

        let msg = Message::from_str(":nick!nick@hostname QUIT :Goodbye!");
        assert_eq!(msg.prefix, Some("nick!nick@hostname".to_owned()));
        assert_eq!(msg.command, Command::Quit);
        assert!(msg.args.len() == 0);
        assert_eq!(msg.suffix, Some("Goodbye!".to_owned()));
    }

    #[test]
    fn weird_cases() {
        let msg = Message::from_str("USER nick:name 0 * hostname");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::User);
        assert_eq!(msg.args, &["nick:name", "0", "*", "hostname"]);
        assert_eq!(msg.suffix, None);

        let msg = Message::from_str("USER  nick  0  *  hostname");
        assert_eq!(msg.prefix, None);
        assert_eq!(msg.command, Command::User);
        assert_eq!(msg.args, &["", "nick", "", "0", "", "*", "", "hostname"]);
        assert_eq!(msg.suffix, None);
    }

    #[test]
    fn utf8_messages() {
        let msg = Message::from_str(":nick!nick@hostname PRIVMSG #foo :Some utf8 fun éèàåöþœðßä");
        assert_eq!(msg.prefix, Some("nick!nick@hostname".to_owned()));
        assert_eq!(msg.command, Command::Privmsg);
        assert_eq!(msg.args, &["#foo"]);
        assert_eq!(msg.suffix, Some("Some utf8 fun éèàåöþœðßä".to_owned()));

        let msg = Message::from_str(":nick!nick@hostname PRIVMSG #héhé :In a chan with utf8 in its name!");
        assert_eq!(msg.prefix, Some("nick!nick@hostname".to_owned()));
        assert_eq!(msg.command, Command::Privmsg);
        assert_eq!(msg.args, &["#héhé"]);
        assert_eq!(msg.suffix, Some("In a chan with utf8 in its name!".to_owned()));
    }
}<|endoftext|>


<commit_before>// Copyright 2015 click2stream, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! PCAP network scanner definitions.

use std::ptr;
use std::fmt;
use std::thread;
use std::result;

use std::error::Error;
use std::thread::JoinHandle;
use std::sync::{Arc, Mutex};
use std::ffi::CString;
use std::fmt::{Display, Formatter};

use utils;

use time;

use libc::{c_int, c_uint, c_long, c_char, c_uchar, c_void, size_t};

/// PCAP module error.
#[derive(Debug)]
pub struct PcapError {
    msg: String,
}

impl PcapError {
    unsafe fn from_cstr(msg: *const c_char) -> PcapError {
        PcapError { msg: utils::cstr_to_string(msg as *const _) }
    }

    fn from_pcap(p: pcap_t) -> PcapError {
        unsafe {
            let cstr = pcap_geterr(p);
            Self::from_cstr(cstr)
        }
    }
}

impl Error for PcapError {
    fn description(&self) -> &str {
        &self.msg
    }
}

impl Display for PcapError {
    fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> {
        f.write_str(&self.msg)
    }
}

impl<'a> From<&'a str> for PcapError {
    fn from(msg: &'a str) -> PcapError {
        PcapError { msg: msg.to_string() }
    }
}

pub type Result<T> = result::Result<T, PcapError>;

#[allow(non_camel_case_types)]
type pcap_t      = *mut c_void;
#[allow(non_camel_case_types)]
type bpf_u_int32 = c_uint;
#[allow(non_camel_case_types)]
type time_t      = c_long;
#[allow(non_camel_case_types)]
type suseconds_t = c_long;

#[repr(C)]
#[allow(non_camel_case_types)]
struct timeval {
    tv_sec:  time_t,
    tv_usec: suseconds_t,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct pcap_pkthdr {
    ts:     timeval,
    caplen: bpf_u_int32,
    len:    bpf_u_int32,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct bpf_program {
    bf_len:   c_uint,
    bf_insns: *mut c_void,
}

impl bpf_program {
    fn new() -> bpf_program {
        bpf_program {
            bf_len:   0,
            bf_insns: ptr::null_mut()
        }
    }
}

#[link(name = "pcap")]
extern "C" {
    fn pcap_create(source: *const c_char, errbuf: *mut c_char) -> pcap_t;
    fn pcap_activate(p: pcap_t) -> c_int;
    fn pcap_close(p: pcap_t) -> c_void;
    fn pcap_geterr(p: pcap_t) -> *const c_char;
    fn pcap_set_promisc(p: pcap_t, promisc: c_int) -> c_int;
    fn pcap_set_timeout(p: pcap_t, ms: c_int) -> c_int;
    fn pcap_next_ex(
        p: pcap_t,
        header: *mut *mut pcap_pkthdr,
        data: *mut *const c_uchar) -> c_int;
    fn pcap_compile(
        p: pcap_t,
        prog: *mut bpf_program,
        pstr: *const c_char,
        optimize: c_int,
        netmask: bpf_u_int32) -> c_int;
    fn pcap_freecode(prog: *mut bpf_program) -> c_void;
    fn pcap_setfilter(p: pcap_t, prog: *mut bpf_program) -> c_int;
    fn pcap_inject(p: pcap_t, buf: *const c_void, size: size_t) -> c_int;
}

/// PCAP context for synchronizing thread unsafe calls.
pub struct Context;

/// PCAP context for synchronizing thread unsafe calls.
pub type ThreadingContext = Arc<Mutex<Context>>;

/// Create a new PCAP context for synchronizing thread unsafe calls.
pub fn new_threading_context() -> ThreadingContext {
    Arc::new(Mutex::new(Context))
}

/// PCAP Capture builder.
pub struct CaptureBuilder {
    capture: Capture,
}

impl CaptureBuilder {
    /// Create a new CaptureBuilder for a given device.
    pub fn new(pc: ThreadingContext, dname: &str) -> Result<CaptureBuilder> {
        let mut result = CaptureBuilder {
            capture: Capture {
                pc:     pc,
                errbuf: Box::new([0; 4096]),
                h:      ptr::null_mut()
            }
        };

        let dname_cstr = CString::new(dname)
            .unwrap()
            .as_ptr() as *const c_char;
        let errbuf_ptr = result.capture.errbuf.as_mut_ptr();
        result.capture.h = unsafe {
            pcap_create(dname_cstr, errbuf_ptr as *mut c_char)
        };

        if result.capture.h.is_null() {
            Err(unsafe { PcapError::from_cstr(errbuf_ptr as *const c_char) })
        } else {
            Ok(result)
        }
    }

    /// Set promiscuous mode.
    pub fn promisc(self, p: bool) -> CaptureBuilder {
        unsafe { pcap_set_promisc(self.capture.h, p as c_int); }
        self
    }

    /// Set timeout.
    pub fn timeout(self, ms: i32) -> CaptureBuilder {
        unsafe { pcap_set_timeout(self.capture.h, ms as c_int); }
        self
    }

    /// Activate.
    pub fn activate(self) -> Result<Capture> {
        let ret = unsafe { pcap_activate(self.capture.h) };
        match ret {
            0 => Ok(self.capture),
            _ => Err(PcapError::from_pcap(self.capture.h))
        }
    }
}

/// Capture result.
pub type CaptureResult = Result<Option<Vec<u8>>>;

/// PCAP capture.
pub struct Capture {
    pc:     ThreadingContext,
    errbuf: Box<[i8; 4096]>,
    h:      pcap_t,
}

impl Capture {
    /// Capture next packet.
    pub fn next(&mut self) -> CaptureResult {
        let mut header: *mut pcap_pkthdr = ptr::null_mut();
        let mut data:   *const c_uchar   = ptr::null();

        unsafe {
            match pcap_next_ex(self.h, &mut header, &mut data) {
                1 => {
                    let href = &*header;
                    let vec  = utils::vec_from_raw_parts(
                        data, href.caplen as usize);
                    Ok(Some(vec))
                },
                0 => Ok(None),
                _ => Err(PcapError::from_pcap(self.h))
            }
        }
    }

    /// Set packet filter.
    pub fn filter(&mut self, f: &str) -> Result<()> {
        unsafe {
            let mut prog = try!(self.compile_filter(f));
            let ret      = pcap_setfilter(self.h, &mut prog);

            pcap_freecode(&mut prog);

            match ret {
                0 => Ok(()),
                _ => Err(PcapError::from_pcap(self.h))
            }
        }
    }

    /// Inject a given raw packet.
    pub fn inject(&mut self, data: &[u8]) -> Result<usize> {
        let ptr = data.as_ptr() as *const c_void;
        let ret = unsafe {
            pcap_inject(self.h, ptr, data.len() as size_t)
        };

        if ret < 0 {
            Err(PcapError::from_pcap(self.h))
        } else {
            Ok(ret as usize)
        }
    }

    /// Compile a given filter string.
    unsafe fn compile_filter(&mut self, f: &str) -> Result<bpf_program> {
        let _lock = self.pc.lock()
            .unwrap();

        let f_cstr = CString::new(f)
            .unwrap()
            .as_ptr() as *const c_char;

        let mut prog = bpf_program::new();

        match pcap_compile(self.h, &mut prog, f_cstr, 0, 0) {
            0 => Ok(prog),
            _ => Err(PcapError::from_pcap(self.h))
        }
    }
}

impl Drop for Capture {
    fn drop(&mut self) {
        unsafe { pcap_close(self.h); }
    }
}

unsafe impl Send for Capture {
}

/// Common trait for packet generators which may be used in combination with
/// the PCAP packet scanner.
pub trait PacketGenerator {
    /// Get next packet.
    fn next<'a>(&'a mut self) -> Option<&'a [u8]>;
}

/// PCAP packet scanner (implementation of a send-receive service).
pub struct Scanner {
    pc:            ThreadingContext,
    device:        String,
    end_indicator: Arc<Mutex<bool>>
}

impl Scanner {
    /// Create a new PCAP scanner for a given device.
    pub fn new(pc: ThreadingContext, device: &str) -> Scanner {
        Scanner {
            pc:            pc,
            device:        device.to_string(),
            end_indicator: Arc::new(Mutex::new(false))
        }
    }

    /// Send all packets from a given iterator and receive all packets
    /// according to a given filter.
    pub fn sr<G: PacketGenerator>(
        &mut self,
        filter: &str,
        gen: &mut G,
        timeout: u64) -> Result<Vec<Vec<u8>>> {
        self.set_end_indicator(false);

        let thread = try!(self.start_listener(filter, timeout));

        try!(self.send_requests(gen));

        self.set_end_indicator(true);

        match thread.join() {
            Err(_)  => Err(PcapError::from("listener thread panicked")),
            Ok(res) => Ok(res)
        }
    }

    /// Start packet listener thread.
    fn start_listener(
        &mut self,
        filter: &str,
        timeout: u64) -> Result<JoinHandle<Vec<Vec<u8>>>> {
        let ei = self.end_indicator.clone();

        let cap = try!(CaptureBuilder::new(self.pc.clone(), &self.device))
            .timeout((timeout / 1000000) as i32)
            .promisc(true);

        let mut cap = try!(cap.activate());

        try!(cap.filter(filter));

        let handle = thread::spawn(move || {
            Self::packet_listener(cap, ei, timeout)
        });

        Ok(handle)
    }

    /// Packet listener thread.
    fn packet_listener(
        mut cap: Capture,
        shared_end_indicator: Arc<Mutex<bool>>,
        timeout: u64) -> Vec<Vec<u8>> {
        let mut vec = Vec::new();
        let mut t   = time::precise_time_ns();
        let mut end = false;

        while !end || time::precise_time_ns() < (t + timeout) {
            match cap.next() {
                Ok(Some(data)) => vec.push(data),
                Err(error)     => panic!(error),
                _ => (),
            }

            if !end && Self::get_end_indicator_value(&shared_end_indicator) {
                t   = time::precise_time_ns();
                end = true;
            }
        }

        vec
    }

    /// Send all pending packets.
    fn send_requests<G: PacketGenerator>(
        &mut self,
        gen: &mut G) -> Result<()> {
        let cap     = try!(CaptureBuilder::new(self.pc.clone(), &self.device));
        let mut cap = try!(cap.activate());

        while let Some(pkt) = gen.next() {
            try!(cap.inject(pkt));
        }

        Ok(())
    }

    /// Set listener end indicator.
    fn set_end_indicator(&mut self, val: bool) {
        let mut end_indicator = self.end_indicator.lock()
            .unwrap();

        *end_indicator = val;
    }

    /// Get end indicator value.
    fn get_end_indicator_value(end_indicator: &Arc<Mutex<bool>>) -> bool {
        let ei = end_indicator.lock()
            .unwrap();

        *ei
    }
}
<commit_msg>Fix segfaults in the libpcap wrapper<commit_after>// Copyright 2015 click2stream, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! PCAP network scanner definitions.

use std::ptr;
use std::fmt;
use std::thread;
use std::result;

use std::error::Error;
use std::thread::JoinHandle;
use std::sync::{Arc, Mutex};
use std::ffi::CString;
use std::fmt::{Display, Formatter};

use utils;

use time;

use libc::{c_int, c_uint, c_long, c_char, c_uchar, c_void, size_t};

/// PCAP module error.
#[derive(Debug)]
pub struct PcapError {
    msg: String,
}

impl PcapError {
    unsafe fn from_cstr(msg: *const c_char) -> PcapError {
        PcapError { msg: utils::cstr_to_string(msg as *const _) }
    }

    fn from_pcap(p: pcap_t) -> PcapError {
        unsafe {
            let cstr = pcap_geterr(p);
            Self::from_cstr(cstr)
        }
    }
}

impl Error for PcapError {
    fn description(&self) -> &str {
        &self.msg
    }
}

impl Display for PcapError {
    fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> {
        f.write_str(&self.msg)
    }
}

impl<'a> From<&'a str> for PcapError {
    fn from(msg: &'a str) -> PcapError {
        PcapError { msg: msg.to_string() }
    }
}

pub type Result<T> = result::Result<T, PcapError>;

#[allow(non_camel_case_types)]
type pcap_t      = *mut c_void;
#[allow(non_camel_case_types)]
type bpf_u_int32 = c_uint;
#[allow(non_camel_case_types)]
type time_t      = c_long;
#[allow(non_camel_case_types)]
type suseconds_t = c_long;

#[repr(C)]
#[allow(non_camel_case_types)]
struct timeval {
    tv_sec:  time_t,
    tv_usec: suseconds_t,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct pcap_pkthdr {
    ts:     timeval,
    caplen: bpf_u_int32,
    len:    bpf_u_int32,
}

#[repr(C)]
#[allow(non_camel_case_types)]
struct bpf_program {
    bf_len:   c_uint,
    bf_insns: *mut c_void,
}

impl bpf_program {
    fn new() -> bpf_program {
        bpf_program {
            bf_len:   0,
            bf_insns: ptr::null_mut()
        }
    }
}

#[link(name = "pcap")]
extern "C" {
    fn pcap_create(source: *const c_char, errbuf: *mut c_char) -> pcap_t;
    fn pcap_activate(p: pcap_t) -> c_int;
    fn pcap_close(p: pcap_t) -> c_void;
    fn pcap_geterr(p: pcap_t) -> *const c_char;
    fn pcap_set_promisc(p: pcap_t, promisc: c_int) -> c_int;
    fn pcap_set_timeout(p: pcap_t, ms: c_int) -> c_int;
    fn pcap_next_ex(
        p: pcap_t,
        header: *mut *mut pcap_pkthdr,
        data: *mut *const c_uchar) -> c_int;
    fn pcap_compile(
        p: pcap_t,
        prog: *mut bpf_program,
        pstr: *const c_char,
        optimize: c_int,
        netmask: bpf_u_int32) -> c_int;
    fn pcap_freecode(prog: *mut bpf_program) -> c_void;
    fn pcap_setfilter(p: pcap_t, prog: *mut bpf_program) -> c_int;
    fn pcap_inject(p: pcap_t, buf: *const c_void, size: size_t) -> c_int;
}

/// PCAP context for synchronizing thread unsafe calls.
pub struct Context;

/// PCAP context for synchronizing thread unsafe calls.
pub type ThreadingContext = Arc<Mutex<Context>>;

/// Create a new PCAP context for synchronizing thread unsafe calls.
pub fn new_threading_context() -> ThreadingContext {
    Arc::new(Mutex::new(Context))
}

/// PCAP Capture builder.
pub struct CaptureBuilder {
    capture: Capture,
}

impl CaptureBuilder {
    /// Create a new CaptureBuilder for a given device.
    pub fn new(pc: ThreadingContext, dname: &str) -> Result<CaptureBuilder> {
        let mut result = CaptureBuilder {
            capture: Capture {
                pc:     pc,
                errbuf: Box::new([0; 4096]),
                h:      ptr::null_mut()
            }
        };

        let dname_cstr = CString::new(dname).unwrap();
        let dname_ptr = dname_cstr.as_ptr() as *const c_char;
        let errbuf_ptr = result.capture.errbuf.as_mut_ptr();
        result.capture.h = unsafe {
            pcap_create(dname_ptr, errbuf_ptr as *mut c_char)
        };

        if result.capture.h.is_null() {
            Err(unsafe { PcapError::from_cstr(errbuf_ptr as *const c_char) })
        } else {
            Ok(result)
        }
    }

    /// Set promiscuous mode.
    pub fn promisc(self, p: bool) -> CaptureBuilder {
        unsafe { pcap_set_promisc(self.capture.h, p as c_int); }
        self
    }

    /// Set timeout.
    pub fn timeout(self, ms: i32) -> CaptureBuilder {
        unsafe { pcap_set_timeout(self.capture.h, ms as c_int); }
        self
    }

    /// Activate.
    pub fn activate(self) -> Result<Capture> {
        let ret = unsafe { pcap_activate(self.capture.h) };
        match ret {
            0 => Ok(self.capture),
            _ => Err(PcapError::from_pcap(self.capture.h))
        }
    }
}

/// Capture result.
pub type CaptureResult = Result<Option<Vec<u8>>>;

/// PCAP capture.
pub struct Capture {
    pc:     ThreadingContext,
    errbuf: Box<[i8; 4096]>,
    h:      pcap_t,
}

impl Capture {
    /// Capture next packet.
    pub fn next(&mut self) -> CaptureResult {
        let mut header: *mut pcap_pkthdr = ptr::null_mut();
        let mut data:   *const c_uchar   = ptr::null();

        unsafe {
            match pcap_next_ex(self.h, &mut header, &mut data) {
                1 => {
                    let href = &*header;
                    let vec  = utils::vec_from_raw_parts(
                        data, href.caplen as usize);
                    Ok(Some(vec))
                },
                0 => Ok(None),
                _ => Err(PcapError::from_pcap(self.h))
            }
        }
    }

    /// Set packet filter.
    pub fn filter(&mut self, f: &str) -> Result<()> {
        unsafe {
            let mut prog = try!(self.compile_filter(f));
            let ret      = pcap_setfilter(self.h, &mut prog);

            pcap_freecode(&mut prog);

            match ret {
                0 => Ok(()),
                _ => Err(PcapError::from_pcap(self.h))
            }
        }
    }

    /// Inject a given raw packet.
    pub fn inject(&mut self, data: &[u8]) -> Result<usize> {
        let ptr = data.as_ptr() as *const c_void;
        let ret = unsafe {
            pcap_inject(self.h, ptr, data.len() as size_t)
        };

        if ret < 0 {
            Err(PcapError::from_pcap(self.h))
        } else {
            Ok(ret as usize)
        }
    }

    /// Compile a given filter string.
    unsafe fn compile_filter(&mut self, f: &str) -> Result<bpf_program> {
        let _lock = self.pc.lock()
            .unwrap();

        let f_cstr = CString::new(f).unwrap();
        let f_ptr = f_cstr.as_ptr() as *const c_char;

        let mut prog = bpf_program::new();

        match pcap_compile(self.h, &mut prog, f_ptr, 0, 0) {
            0 => Ok(prog),
            _ => Err(PcapError::from_pcap(self.h))
        }
    }
}

impl Drop for Capture {
    fn drop(&mut self) {
        if !self.h.is_null() {
            unsafe { pcap_close(self.h); }
        }
    }
}

unsafe impl Send for Capture {
}

/// Common trait for packet generators which may be used in combination with
/// the PCAP packet scanner.
pub trait PacketGenerator {
    /// Get next packet.
    fn next<'a>(&'a mut self) -> Option<&'a [u8]>;
}

/// PCAP packet scanner (implementation of a send-receive service).
pub struct Scanner {
    pc:            ThreadingContext,
    device:        String,
    end_indicator: Arc<Mutex<bool>>
}

impl Scanner {
    /// Create a new PCAP scanner for a given device.
    pub fn new(pc: ThreadingContext, device: &str) -> Scanner {
        Scanner {
            pc:            pc,
            device:        device.to_string(),
            end_indicator: Arc::new(Mutex::new(false))
        }
    }

    /// Send all packets from a given iterator and receive all packets
    /// according to a given filter.
    pub fn sr<G: PacketGenerator>(
        &mut self,
        filter: &str,
        gen: &mut G,
        timeout: u64) -> Result<Vec<Vec<u8>>> {
        self.set_end_indicator(false);

        let thread = try!(self.start_listener(filter, timeout));

        try!(self.send_requests(gen));

        self.set_end_indicator(true);

        match thread.join() {
            Err(_)  => Err(PcapError::from("listener thread panicked")),
            Ok(res) => Ok(res)
        }
    }

    /// Start packet listener thread.
    fn start_listener(
        &mut self,
        filter: &str,
        timeout: u64) -> Result<JoinHandle<Vec<Vec<u8>>>> {
        let ei = self.end_indicator.clone();

        let cap = try!(CaptureBuilder::new(self.pc.clone(), &self.device))
            .timeout((timeout / 1000000) as i32)
            .promisc(true);

        let mut cap = try!(cap.activate());

        try!(cap.filter(filter));

        let handle = thread::spawn(move || {
            Self::packet_listener(cap, ei, timeout)
        });

        Ok(handle)
    }

    /// Packet listener thread.
    fn packet_listener(
        mut cap: Capture,
        shared_end_indicator: Arc<Mutex<bool>>,
        timeout: u64) -> Vec<Vec<u8>> {
        let mut vec = Vec::new();
        let mut t   = time::precise_time_ns();
        let mut end = false;

        while !end || time::precise_time_ns() < (t + timeout) {
            match cap.next() {
                Ok(Some(data)) => vec.push(data),
                Err(error)     => panic!(error),
                _ => (),
            }

            if !end && Self::get_end_indicator_value(&shared_end_indicator) {
                t   = time::precise_time_ns();
                end = true;
            }
        }

        vec
    }

    /// Send all pending packets.
    fn send_requests<G: PacketGenerator>(
        &mut self,
        gen: &mut G) -> Result<()> {
        let cap     = try!(CaptureBuilder::new(self.pc.clone(), &self.device));
        let mut cap = try!(cap.activate());

        while let Some(pkt) = gen.next() {
            try!(cap.inject(pkt));
        }

        Ok(())
    }

    /// Set listener end indicator.
    fn set_end_indicator(&mut self, val: bool) {
        let mut end_indicator = self.end_indicator.lock()
            .unwrap();

        *end_indicator = val;
    }

    /// Get end indicator value.
    fn get_end_indicator_value(end_indicator: &Arc<Mutex<bool>>) -> bool {
        let ei = end_indicator.lock()
            .unwrap();

        *ei
    }
}
<|endoftext|>


<commit_before>use super::wasm;
use anyhow::Context;
use std::env;
use std::fs;
use std::net::TcpListener;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use tiny_http::{Header, Response, Server};
use webbrowser;

macro_rules! resource {
    ($name: tt, $path: tt) => {
        #[cfg(TREE_SITTER_EMBED_WASM_BINDING)]
        fn $name(tree_sitter_dir: &Option<PathBuf>) -> Vec<u8> {
            if let Some(tree_sitter_dir) = tree_sitter_dir {
                fs::read(tree_sitter_dir.join($path)).unwrap()
            } else {
                include_bytes!(concat!("../../", $path)).to_vec()
            }
        }

        #[cfg(not(TREE_SITTER_EMBED_WASM_BINDING))]
        fn $name(tree_sitter_dir: &Option<PathBuf>) -> Vec<u8> {
            if let Some(tree_sitter_dir) = tree_sitter_dir {
                fs::read(tree_sitter_dir.join($path)).unwrap()
            } else {
                Vec::new()
            }
        }
    };
}

resource!(get_main_html, "cli/src/playground.html");
resource!(get_playground_js, "docs/assets/js/playground.js");
resource!(get_lib_js, "lib/binding_web/tree-sitter.js");
resource!(get_lib_wasm, "lib/binding_web/tree-sitter.wasm");

pub fn serve(grammar_path: &Path, open_in_browser: bool) {
    let port = get_available_port().expect("Couldn't find an available port");
    let addr = format!("127.0.0.1:{}", port);
    let server = Server::http(&addr).expect("Failed to start web server");
    let grammar_name = wasm::get_grammar_name(&grammar_path.join("src"))
        .with_context(|| "Failed to get wasm filename")
        .unwrap();
    let wasm_filename = format!("tree-sitter-{}.wasm", grammar_name);
    let language_wasm = fs::read(grammar_path.join(&wasm_filename))
        .with_context(|| {
            format!(
                "Failed to read {}. Run `tree-sitter build-wasm` first.",
                wasm_filename
            )
        })
        .unwrap();
    let url = format!("http://{}", addr);
    println!("Started playground on: {}", url);
    if open_in_browser {
        if let Err(_) = webbrowser::open(&url) {
            eprintln!("Failed to open '{}' in a web browser", url);
        }
    }

    let tree_sitter_dir = env::var("TREE_SITTER_BASE_DIR").map(PathBuf::from).ok();
    let main_html = String::from_utf8(get_main_html(&tree_sitter_dir))
        .unwrap()
        .replace("THE_LANGUAGE_NAME", &grammar_name)
        .into_bytes();
    let playground_js = get_playground_js(&tree_sitter_dir);
    let lib_js = get_lib_js(&tree_sitter_dir);
    let lib_wasm = get_lib_wasm(&tree_sitter_dir);

    let html_header = Header::from_str("Content-Type: text/html").unwrap();
    let js_header = Header::from_str("Content-Type: application/javascript").unwrap();
    let wasm_header = Header::from_str("Content-Type: application/wasm").unwrap();

    for request in server.incoming_requests() {
        let res = match request.url() {
            "/" => response(&main_html, &html_header),
            "/tree-sitter-parser.wasm" => response(&language_wasm, &wasm_header),
            "/playground.js" => {
                if playground_js.is_empty() {
                    redirect("https://tree-sitter.github.io/tree-sitter/assets/js/playground.js")
                } else {
                    response(&playground_js, &js_header)
                }
            }
            "/tree-sitter.js" => {
                if lib_js.is_empty() {
                    redirect("https://tree-sitter.github.io/tree-sitter.js")
                } else {
                    response(&lib_js, &js_header)
                }
            }
            "/tree-sitter.wasm" => {
                if lib_wasm.is_empty() {
                    redirect("https://tree-sitter.github.io/tree-sitter.wasm")
                } else {
                    response(&lib_wasm, &wasm_header)
                }
            }
            _ => response(b"Not found", &html_header).with_status_code(404),
        };
        request.respond(res).expect("Failed to write HTTP response");
    }
}

fn redirect<'a>(url: &'a str) -> Response<&'a [u8]> {
    Response::empty(302)
        .with_data("".as_bytes(), Some(0))
        .with_header(Header::from_bytes("Location", url.as_bytes()).unwrap())
}

fn response<'a>(data: &'a [u8], header: &Header) -> Response<&'a [u8]> {
    Response::empty(200)
        .with_data(data, Some(data.len()))
        .with_header(header.clone())
}

fn get_available_port() -> Option<u16> {
    (8000..12000).find(port_is_available)
}

fn port_is_available(port: &u16) -> bool {
    TcpListener::bind(("127.0.0.1", *port)).is_ok()
}
<commit_msg>Always include playground.html in the CLI binary<commit_after>use super::wasm;
use anyhow::Context;
use std::{
    borrow::Cow,
    env, fs,
    net::TcpListener,
    path::{Path, PathBuf},
    str::{self, FromStr as _},
};
use tiny_http::{Header, Response, Server};
use webbrowser;

macro_rules! resource {
    ($name: tt, $path: tt) => {
        fn $name(tree_sitter_dir: &Option<PathBuf>) -> Cow<'static, [u8]> {
            if let Some(tree_sitter_dir) = tree_sitter_dir {
                Cow::Owned(fs::read(tree_sitter_dir.join($path)).unwrap())
            } else {
                Cow::Borrowed(include_bytes!(concat!("../../", $path)))
            }
        }
    };
}

macro_rules! optional_resource {
    ($name: tt, $path: tt) => {
        #[cfg(TREE_SITTER_EMBED_WASM_BINDING)]
        resource!($name, $path);

        #[cfg(not(TREE_SITTER_EMBED_WASM_BINDING))]
        fn $name(tree_sitter_dir: &Option<PathBuf>) -> Cow<'static, [u8]> {
            if let Some(tree_sitter_dir) = tree_sitter_dir {
                Cow::Owned(fs::read(tree_sitter_dir.join($path)).unwrap())
            } else {
                Cow::Borrowed(&[])
            }
        }
    };
}

resource!(get_main_html, "cli/src/playground.html");
optional_resource!(get_playground_js, "docs/assets/js/playground.js");
optional_resource!(get_lib_js, "lib/binding_web/tree-sitter.js");
optional_resource!(get_lib_wasm, "lib/binding_web/tree-sitter.wasm");

pub fn serve(grammar_path: &Path, open_in_browser: bool) {
    let port = get_available_port().expect("Couldn't find an available port");
    let addr = format!("127.0.0.1:{}", port);
    let server = Server::http(&addr).expect("Failed to start web server");
    let grammar_name = wasm::get_grammar_name(&grammar_path.join("src"))
        .with_context(|| "Failed to get wasm filename")
        .unwrap();
    let wasm_filename = format!("tree-sitter-{}.wasm", grammar_name);
    let language_wasm = fs::read(grammar_path.join(&wasm_filename))
        .with_context(|| {
            format!(
                "Failed to read {}. Run `tree-sitter build-wasm` first.",
                wasm_filename
            )
        })
        .unwrap();
    let url = format!("http://{}", addr);
    println!("Started playground on: {}", url);
    if open_in_browser {
        if let Err(_) = webbrowser::open(&url) {
            eprintln!("Failed to open '{}' in a web browser", url);
        }
    }

    let tree_sitter_dir = env::var("TREE_SITTER_BASE_DIR").map(PathBuf::from).ok();
    let main_html = str::from_utf8(&get_main_html(&tree_sitter_dir))
        .unwrap()
        .replace("THE_LANGUAGE_NAME", &grammar_name)
        .into_bytes();
    let playground_js = get_playground_js(&tree_sitter_dir);
    let lib_js = get_lib_js(&tree_sitter_dir);
    let lib_wasm = get_lib_wasm(&tree_sitter_dir);

    let html_header = Header::from_str("Content-Type: text/html").unwrap();
    let js_header = Header::from_str("Content-Type: application/javascript").unwrap();
    let wasm_header = Header::from_str("Content-Type: application/wasm").unwrap();

    for request in server.incoming_requests() {
        let res = match request.url() {
            "/" => response(&main_html, &html_header),
            "/tree-sitter-parser.wasm" => response(&language_wasm, &wasm_header),
            "/playground.js" => {
                if playground_js.is_empty() {
                    redirect("https://tree-sitter.github.io/tree-sitter/assets/js/playground.js")
                } else {
                    response(&playground_js, &js_header)
                }
            }
            "/tree-sitter.js" => {
                if lib_js.is_empty() {
                    redirect("https://tree-sitter.github.io/tree-sitter.js")
                } else {
                    response(&lib_js, &js_header)
                }
            }
            "/tree-sitter.wasm" => {
                if lib_wasm.is_empty() {
                    redirect("https://tree-sitter.github.io/tree-sitter.wasm")
                } else {
                    response(&lib_wasm, &wasm_header)
                }
            }
            _ => response(b"Not found", &html_header).with_status_code(404),
        };
        request.respond(res).expect("Failed to write HTTP response");
    }
}

fn redirect<'a>(url: &'a str) -> Response<&'a [u8]> {
    Response::empty(302)
        .with_data("".as_bytes(), Some(0))
        .with_header(Header::from_bytes("Location", url.as_bytes()).unwrap())
}

fn response<'a>(data: &'a [u8], header: &Header) -> Response<&'a [u8]> {
    Response::empty(200)
        .with_data(data, Some(data.len()))
        .with_header(header.clone())
}

fn get_available_port() -> Option<u16> {
    (8000..12000).find(port_is_available)
}

fn port_is_available(port: &u16) -> bool {
    TcpListener::bind(("127.0.0.1", *port)).is_ok()
}
<|endoftext|>


<commit_before>#[cfg(feature = "with-tremor")]
extern crate librespot_tremor as vorbis;
#[cfg(not(feature = "with-tremor"))]
extern crate vorbis;

use super::{AudioDecoder, AudioError, AudioPacket};
use std::error;
use std::fmt;
use std::io::{Read, Seek};

pub struct VorbisDecoder<R: Read + Seek>(vorbis::Decoder<R>);
pub struct VorbisError(vorbis::VorbisError);

impl<R> VorbisDecoder<R>
where
    R: Read + Seek,
{
    pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
        Ok(VorbisDecoder(vorbis::Decoder::new(input)?))
    }
}

impl<R> AudioDecoder for VorbisDecoder<R>
where
    R: Read + Seek,
{
    #[cfg(not(feature = "with-tremor"))]
    fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
        self.0.time_seek(ms as f64 / 1000f64)?;
        Ok(())
    }

    #[cfg(feature = "with-tremor")]
    fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
        self.0.time_seek(ms)?;
        Ok(())
    }

    fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError> {
        loop {
            match self.0.packets().next() {
                Some(Ok(packet)) => {
                    // Losslessly represent [-32768, 32767] to [-1.0, 1.0] while maintaining DC linearity.
                    return Ok(Some(AudioPacket::Samples(
                        packet
                            .data
                            .iter()
                            .map(|sample| ((*sample as f64 + 0.5) / (0x7FFF as f64 + 0.5)) as f32)
                            .collect(),
                    )));
                }
                None => return Ok(None),

                Some(Err(vorbis::VorbisError::Hole)) => (),
                Some(Err(err)) => return Err(err.into()),
            }
        }
    }
}

impl From<vorbis::VorbisError> for VorbisError {
    fn from(err: vorbis::VorbisError) -> VorbisError {
        VorbisError(err)
    }
}

impl fmt::Debug for VorbisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

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

impl error::Error for VorbisError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}

impl From<vorbis::VorbisError> for AudioError {
    fn from(err: vorbis::VorbisError) -> AudioError {
        AudioError::VorbisError(VorbisError(err))
    }
}
<commit_msg>Make S16 to F32 sample conversion less magical<commit_after>#[cfg(feature = "with-tremor")]
extern crate librespot_tremor as vorbis;
#[cfg(not(feature = "with-tremor"))]
extern crate vorbis;

use super::{AudioDecoder, AudioError, AudioPacket};
use std::error;
use std::fmt;
use std::io::{Read, Seek};

pub struct VorbisDecoder<R: Read + Seek>(vorbis::Decoder<R>);
pub struct VorbisError(vorbis::VorbisError);

impl<R> VorbisDecoder<R>
where
    R: Read + Seek,
{
    pub fn new(input: R) -> Result<VorbisDecoder<R>, VorbisError> {
        Ok(VorbisDecoder(vorbis::Decoder::new(input)?))
    }
}

impl<R> AudioDecoder for VorbisDecoder<R>
where
    R: Read + Seek,
{
    #[cfg(not(feature = "with-tremor"))]
    fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
        self.0.time_seek(ms as f64 / 1000f64)?;
        Ok(())
    }

    #[cfg(feature = "with-tremor")]
    fn seek(&mut self, ms: i64) -> Result<(), AudioError> {
        self.0.time_seek(ms)?;
        Ok(())
    }

    fn next_packet(&mut self) -> Result<Option<AudioPacket>, AudioError> {
        loop {
            match self.0.packets().next() {
                Some(Ok(packet)) => {
                    // Losslessly represent [-32768, 32767] to [-1.0, 1.0] while maintaining DC linearity.
                    return Ok(Some(AudioPacket::Samples(
                        packet
                            .data
                            .iter()
                            .map(|sample| ((*sample as f64 + 0.5) / (std::i16::MAX as f64 + 0.5)) as f32)
                            .collect(),
                    )));
                }
                None => return Ok(None),

                Some(Err(vorbis::VorbisError::Hole)) => (),
                Some(Err(err)) => return Err(err.into()),
            }
        }
    }
}

impl From<vorbis::VorbisError> for VorbisError {
    fn from(err: vorbis::VorbisError) -> VorbisError {
        VorbisError(err)
    }
}

impl fmt::Debug for VorbisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

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

impl error::Error for VorbisError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}

impl From<vorbis::VorbisError> for AudioError {
    fn from(err: vorbis::VorbisError) -> AudioError {
        AudioError::VorbisError(VorbisError(err))
    }
}
<|endoftext|>


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

use std::env;
use std::path::{Path, PathBuf};
use std::io::prelude::*;
use std::fs::{canonicalize, read_dir, File};

fn write_shaders(glsl_files: Vec<PathBuf>, shader_file_path: &Path) {
    let mut shader_file = File::create(shader_file_path).unwrap();

    write!(shader_file, "/// AUTO GENERATED BY build.rs\n\n").unwrap();
    write!(shader_file, "use std::collections::HashMap;\n").unwrap();
    write!(shader_file, "lazy_static! {{\n").unwrap();
    write!(shader_file, "  pub static ref SHADERS: HashMap<&'static str, &'static str> = {{\n").unwrap();
    write!(shader_file, "    let mut h = HashMap::with_capacity({});\n", glsl_files.len()).unwrap();
    for glsl in glsl_files {
        let shader_name = glsl.file_name().unwrap().to_str().unwrap();
        // strip .glsl
        let shader_name = shader_name.replace(".glsl", "");
        let full_path = canonicalize(&glsl).unwrap();
        let full_name = full_path.as_os_str().to_str().unwrap();
        // if someone is building on a network share, I'm sorry.
        let full_name = full_name.replace("\\\\?\\", "");
        let full_name = full_name.replace("\\", "/");
        write!(shader_file, "    h.insert(\"{}\", include_str!(\"{}\"));\n",
               shader_name, full_name).unwrap();
    }
    write!(shader_file, "    h\n").unwrap();
    write!(shader_file, "  }};\n").unwrap();
    write!(shader_file, "}}\n").unwrap();
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());

    let shaders_file = Path::new(&out_dir).join("shaders.rs");
    let mut glsl_files = vec![];

    println!("cargo:rerun-if-changed=res");
    let res_dir = Path::new("res");
    for entry in read_dir(res_dir).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();

        if entry.file_name().to_str().unwrap().ends_with(".glsl") {
            println!("cargo:rerun-if-changed={}", path.display());
            glsl_files.push(path.to_owned());
        }
    }

    write_shaders(glsl_files, &shaders_file);
}
<commit_msg>Sort the list of resource files for sharers.rs to be deterministic<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::env;
use std::path::{Path, PathBuf};
use std::io::prelude::*;
use std::fs::{canonicalize, read_dir, File};

fn write_shaders(glsl_files: Vec<PathBuf>, shader_file_path: &Path) {
    let mut shader_file = File::create(shader_file_path).unwrap();

    write!(shader_file, "/// AUTO GENERATED BY build.rs\n\n").unwrap();
    write!(shader_file, "use std::collections::HashMap;\n").unwrap();
    write!(shader_file, "lazy_static! {{\n").unwrap();
    write!(shader_file, "  pub static ref SHADERS: HashMap<&'static str, &'static str> = {{\n").unwrap();
    write!(shader_file, "    let mut h = HashMap::with_capacity({});\n", glsl_files.len()).unwrap();
    for glsl in glsl_files {
        let shader_name = glsl.file_name().unwrap().to_str().unwrap();
        // strip .glsl
        let shader_name = shader_name.replace(".glsl", "");
        let full_path = canonicalize(&glsl).unwrap();
        let full_name = full_path.as_os_str().to_str().unwrap();
        // if someone is building on a network share, I'm sorry.
        let full_name = full_name.replace("\\\\?\\", "");
        let full_name = full_name.replace("\\", "/");
        write!(shader_file, "    h.insert(\"{}\", include_str!(\"{}\"));\n",
               shader_name, full_name).unwrap();
    }
    write!(shader_file, "    h\n").unwrap();
    write!(shader_file, "  }};\n").unwrap();
    write!(shader_file, "}}\n").unwrap();
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());

    let shaders_file = Path::new(&out_dir).join("shaders.rs");
    let mut glsl_files = vec![];

    println!("cargo:rerun-if-changed=res");
    let res_dir = Path::new("res");
    for entry in read_dir(res_dir).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();

        if entry.file_name().to_str().unwrap().ends_with(".glsl") {
            println!("cargo:rerun-if-changed={}", path.display());
            glsl_files.push(path.to_owned());
        }
    }

    // Sort the file list so that the shaders.rs file is filled
    // deterministically.
    glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));

    write_shaders(glsl_files, &shaders_file);
}
<|endoftext|>


<commit_before>use crate::grammar::ast::{Parens, Expression};
use crate::grammar::model::{HasFragment, Fragment};
use nom::IResult;
use nom::combinator::{map, recognize};
use nom::sequence::delimited;
use nom::character::complete::{
    space0, char as ch
};

impl<'s> Parens<'s> {
    fn new(frag: Fragment<'s>, inner: Box<Expression<'s>>) -> Self {
        Self {frag, inner}
    }

    fn inner(frag: Fragment<'s>) -> IResult<Fragment<'s>, Expression<'s>> {
        delimited(
            space0, // change to general whitespace
            delimited(ch('('), Expression::parse, ch(')')),
            space0, // change to general whitespace
        )(frag)
    }

    /// Parse parentheses and the expression between them in source code. Will
    /// ignore any whitespace before and after.
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            recognize(Self::inner),
            |parse| {
                Parens {
                    frag: parse,
                    inner: Box::new(Self::inner(parse).unwrap().1),
                }
            },
        )(input)
    }
}

impl<'s> HasFragment<'s> for Parens<'s> {
    fn get_fragment(&self) -> Fragment<'s> {self.frag}
}<commit_msg>replace space0 with multispace0<commit_after>use crate::grammar::ast::{Parens, Expression};
use crate::grammar::model::{HasFragment, Fragment};
use nom::IResult;
use nom::combinator::{map, recognize};
use nom::sequence::delimited;
use nom::character::complete::{char as ch, multispace0};

impl<'s> Parens<'s> {
    fn new(frag: Fragment<'s>, inner: Box<Expression<'s>>) -> Self {
        Self {frag, inner}
    }

    fn inner(frag: Fragment<'s>) -> IResult<Fragment<'s>, Expression<'s>> {
        delimited(
            multispace0,
            delimited(ch('('), Expression::parse, ch(')')),
            multispace0,
        )(frag)
    }

    /// Parse parentheses and the expression between them in source code. Will
    /// ignore any whitespace before and after.
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            recognize(Self::inner),
            |parse| {
                Parens {
                    frag: parse,
                    inner: Box::new(Self::inner(parse).unwrap().1),
                }
            },
        )(input)
    }
}

impl<'s> HasFragment<'s> for Parens<'s> {
    fn get_fragment(&self) -> Fragment<'s> {self.frag}
}<|endoftext|>