<commit_before>extern crate reqwest;
extern crate serde;
extern crate serde_json;
extern crate regex;

use std::io::Read;
use irc::client::prelude::*;
use irc::error::Error as IrcError;
use plugin::Plugin;
use self::reqwest::Client;
use self::reqwest::header::Connection;
use self::serde_json::Value;

use PluginCommand;

register_plugin!(Currency);

struct ConvertionRequest<'a> {
    value: f64,
    source: &'a str,
    target: &'a str,
}

macro_rules! try_option {
    ($e:expr) => {
        match $e {
            Some(v) => v,
            None    => { return None; }
        }
    }
}

impl<'a> ConvertionRequest<'a> {
    fn send(&self) -> Option<f64> {

        let response = Client::new()
            .get("https://api.fixer.io/latest")
            .form(&[("base", self.source)])
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                try_option!(response.read_to_string(&mut body).ok());

                let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
                match convertion_rates {
                    Ok(convertion_rates) => {

                        let rates: &Value = try_option!(convertion_rates.get("rates"));
                        let target_rate: &Value =
                            try_option!(rates.get(self.target.to_uppercase()));
                        Some(self.value * try_option!(target_rate.as_f64()))
                    }
                    Err(_) => None,
                }
            }
            Err(_) => None,
        }
    }
}

impl Currency {
    fn eval_command<'a>(&self, tokens: &'a [String]) -> Option<ConvertionRequest<'a>> {
        let parsed = match tokens[0].parse() {
            Ok(v) => v,
            Err(_) => {
                return None;
            }
        };

        Some(ConvertionRequest {
                 value: parsed,
                 source: &tokens[1],
                 target: &tokens[2],
             })
    }

    fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let request = match self.eval_command(&command.tokens) {
            Some(request) => request,
            None => {
                return self.invalid_command(server, &command);
            }
        };

        match request.send() {
            Some(response) => {
                let response = format!("{} {} => {:.4} {}",
                                       request.value,
                                       request.source.to_lowercase(),
                                       response / 1.00000000,
                                       request.target.to_lowercase());

                server.send_privmsg(&command.target, &response)
            }
            None => server.send_privmsg(&command.target, "Error while converting given currency"),
        }
    }

    fn help(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let usage = format!("usage: {} currency value from_currency to_currency",
                            server.current_nickname());

        if let Err(e) = server.send_notice(&command.source, &usage) {
            return Err(e);
        }
        server.send_notice(&command.source, "example: 1.5 eur usd")
    }

    fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
        let help = format!("Incorrect value. \
                           Send \"{} help currency\" for help.",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }
}

impl Plugin for Currency {
    fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
        false
    }

    fn execute(&mut self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
        Ok(())
    }

    fn command(&mut self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        if command.tokens.is_empty() {
            self.invalid_command(server, &command)

        } else if command.tokens[0].to_lowercase() == "help" {
            self.help(server, command)

        } else if command.tokens.len() >= 3 {
            self.convert(server, command)

        } else {
            self.invalid_command(server, &command)
        }
    }
}

#[cfg(test)]
mod tests {
    //use tests::{make_server, get_server_value};

    //use irc::client::prelude::*;

    //use plugin::Plugin;
    //use regex::Regex;
    //use super::Currency;

    //#[test]
    //fn test_big_jpy_to_eur() {
    //    let server = make_server("PRIVMSG test :5000000 JPY to EUR\r\n");
    //    let mut plugin = Currency::new();

    //    for message in server.iter() {
    //        let message = message.unwrap();
    //        assert!(plugin.is_allowed(&server, &message));
    //        assert!(plugin.execute(&server, &message).is_ok());
    //    }

    //    let regex = Regex::new(r"=> ([0-9]{2})").unwrap();
    //    let msg = get_server_value(&server);
    //    let captures = regex.captures(&*msg).unwrap();
    //    assert!(captures.at(0).is_some());

    //    let result = captures
    //        .at(0)
    //        .unwrap()
    //        .split_whitespace()
    //        .last()
    //        .unwrap()
    //        .parse::<i32>();
    //    assert!(result.is_ok());

    //    let value = result.unwrap();
    //    assert!(value > 10 && value < 100);
    //}
}
<commit_msg>Changed currency error into a notice<commit_after>extern crate reqwest;
extern crate serde;
extern crate serde_json;
extern crate regex;

use std::io::Read;
use irc::client::prelude::*;
use irc::error::Error as IrcError;
use plugin::Plugin;
use self::reqwest::Client;
use self::reqwest::header::Connection;
use self::serde_json::Value;

use PluginCommand;

register_plugin!(Currency);

struct ConvertionRequest<'a> {
    value: f64,
    source: &'a str,
    target: &'a str,
}

macro_rules! try_option {
    ($e:expr) => {
        match $e {
            Some(v) => v,
            None    => { return None; }
        }
    }
}

impl<'a> ConvertionRequest<'a> {
    fn send(&self) -> Option<f64> {

        let response = Client::new()
            .get("https://api.fixer.io/latest")
            .form(&[("base", self.source)])
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                try_option!(response.read_to_string(&mut body).ok());

                let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
                match convertion_rates {
                    Ok(convertion_rates) => {

                        let rates: &Value = try_option!(convertion_rates.get("rates"));
                        let target_rate: &Value =
                            try_option!(rates.get(self.target.to_uppercase()));
                        Some(self.value * try_option!(target_rate.as_f64()))
                    }
                    Err(_) => None,
                }
            }
            Err(_) => None,
        }
    }
}

impl Currency {
    fn eval_command<'a>(&self, tokens: &'a [String]) -> Option<ConvertionRequest<'a>> {
        let parsed = match tokens[0].parse() {
            Ok(v) => v,
            Err(_) => {
                return None;
            }
        };

        Some(ConvertionRequest {
                 value: parsed,
                 source: &tokens[1],
                 target: &tokens[2],
             })
    }

    fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let request = match self.eval_command(&command.tokens) {
            Some(request) => request,
            None => {
                return self.invalid_command(server, &command);
            }
        };

        match request.send() {
            Some(response) => {
                let response = format!("{} {} => {:.4} {}",
                                       request.value,
                                       request.source.to_lowercase(),
                                       response / 1.00000000,
                                       request.target.to_lowercase());

                server.send_privmsg(&command.target, &response)
            }
            None => server.send_notice(&command.source, "Error while converting given currency"),
        }
    }

    fn help(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let usage = format!("usage: {} currency value from_currency to_currency",
                            server.current_nickname());

        if let Err(e) = server.send_notice(&command.source, &usage) {
            return Err(e);
        }
        server.send_notice(&command.source, "example: 1.5 eur usd")
    }

    fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
        let help = format!("Incorrect value. \
                           Send \"{} help currency\" for help.",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }
}

impl Plugin for Currency {
    fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
        false
    }

    fn execute(&mut self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
        Ok(())
    }

    fn command(&mut self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        if command.tokens.is_empty() {
            self.invalid_command(server, &command)

        } else if command.tokens[0].to_lowercase() == "help" {
            self.help(server, command)

        } else if command.tokens.len() >= 3 {
            self.convert(server, command)

        } else {
            self.invalid_command(server, &command)
        }
    }
}

#[cfg(test)]
mod tests {
    //use tests::{make_server, get_server_value};

    //use irc::client::prelude::*;

    //use plugin::Plugin;
    //use regex::Regex;
    //use super::Currency;

    //#[test]
    //fn test_big_jpy_to_eur() {
    //    let server = make_server("PRIVMSG test :5000000 JPY to EUR\r\n");
    //    let mut plugin = Currency::new();

    //    for message in server.iter() {
    //        let message = message.unwrap();
    //        assert!(plugin.is_allowed(&server, &message));
    //        assert!(plugin.execute(&server, &message).is_ok());
    //    }

    //    let regex = Regex::new(r"=> ([0-9]{2})").unwrap();
    //    let msg = get_server_value(&server);
    //    let captures = regex.captures(&*msg).unwrap();
    //    assert!(captures.at(0).is_some());

    //    let result = captures
    //        .at(0)
    //        .unwrap()
    //        .split_whitespace()
    //        .last()
    //        .unwrap()
    //        .parse::<i32>();
    //    assert!(result.is_ok());

    //    let value = result.unwrap();
    //    assert!(value > 10 && value < 100);
    //}
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::path::{Path, PathBuf};

use crate::prelude::*;

use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
use rustc_codegen_ssa::METADATA_FILENAME;

struct ArchiveConfig<'a> {
    sess: &'a Session,
    dst: PathBuf,
    lib_search_paths: Vec<PathBuf>,
    use_native_ar: bool,
    use_gnu_style_archive: bool,
}

#[derive(Debug)]
enum ArchiveEntry {
    FromArchive {
        archive_index: usize,
        entry_index: usize,
    },
    File(PathBuf),
}

pub(crate) struct ArArchiveBuilder<'a> {
    config: ArchiveConfig<'a>,
    src_archives: Vec<(PathBuf, ar::Archive<File>)>,
    // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
    // the end of an archive for linkers to not get confused.
    entries: Vec<(String, ArchiveEntry)>,
    update_symbols: bool,
}

impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
    fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
        use rustc_codegen_ssa::back::link::archive_search_paths;
        let config = ArchiveConfig {
            sess,
            dst: output.to_path_buf(),
            lib_search_paths: archive_search_paths(sess),
            use_native_ar: false,
            // FIXME test for linux and System V derivatives instead
            use_gnu_style_archive: sess.target.target.options.archive_format == "gnu",
        };

        let (src_archives, entries) = if let Some(input) = input {
            let mut archive = ar::Archive::new(File::open(input).unwrap());
            let mut entries = Vec::new();

            let mut i = 0;
            while let Some(entry) = archive.next_entry() {
                let entry = entry.unwrap();
                entries.push((
                    String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
                    ArchiveEntry::FromArchive {
                        archive_index: 0,
                        entry_index: i,
                    },
                ));
                i += 1;
            }

            (vec![(input.to_owned(), archive)], entries)
        } else {
            (vec![], Vec::new())
        };

        ArArchiveBuilder {
            config,
            src_archives,
            entries,
            update_symbols: false,
        }
    }

    fn src_files(&mut self) -> Vec<String> {
        self.entries.iter().map(|(name, _)| name.clone()).collect()
    }

    fn remove_file(&mut self, name: &str) {
        let index = self
            .entries
            .iter()
            .position(|(entry_name, _)| entry_name == name)
            .expect("Tried to remove file not existing in src archive");
        self.entries.remove(index);
    }

    fn add_file(&mut self, file: &Path) {
        self.entries.push((
            file.file_name().unwrap().to_str().unwrap().to_string(),
            ArchiveEntry::File(file.to_owned()),
        ));
    }

    fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) {
        let location = find_library(name, &self.config.lib_search_paths, self.config.sess);
        self.add_archive(location.clone(), |_| false)
            .unwrap_or_else(|e| {
                panic!(
                    "failed to add native library {}: {}",
                    location.to_string_lossy(),
                    e
                );
            });
    }

    fn add_rlib(
        &mut self,
        rlib: &Path,
        name: &str,
        lto: bool,
        skip_objects: bool,
    ) -> std::io::Result<()> {
        let obj_start = name.to_owned();

        self.add_archive(rlib.to_owned(), move |fname: &str| {
            // Ignore metadata files, no matter the name.
            if fname == METADATA_FILENAME {
                return true;
            }

            // Don't include Rust objects if LTO is enabled
            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
                return true;
            }

            // Otherwise if this is *not* a rust object and we're skipping
            // objects then skip this file
            if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
                return true;
            }

            // ok, don't skip this
            return false;
        })
    }

    fn update_symbols(&mut self) {
        self.update_symbols = true;
    }

    fn build(mut self) {
        use std::process::Command;

        fn add_file_using_ar(archive: &Path, file: &Path) {
            Command::new("ar")
                .arg("r") // add or replace file
                .arg("-c") // silence created file message
                .arg(archive)
                .arg(&file)
                .status()
                .unwrap();
        }

        enum BuilderKind<'a> {
            Bsd(ar::Builder<File>),
            Gnu(ar::GnuBuilder<File>),
            NativeAr(&'a Path),
        }

        let mut builder = if self.config.use_native_ar {
            BuilderKind::NativeAr(&self.config.dst)
        } else if self.config.use_gnu_style_archive {
            BuilderKind::Gnu(ar::GnuBuilder::new(
                File::create(&self.config.dst).unwrap(),
                self.entries
                    .iter()
                    .map(|(name, _)| name.as_bytes().to_vec())
                    .collect(),
            ))
        } else {
            BuilderKind::Bsd(ar::Builder::new(File::create(&self.config.dst).unwrap()))
        };

        // Add all files
        for (entry_name, entry) in self.entries.into_iter() {
            match entry {
                ArchiveEntry::FromArchive {
                    archive_index,
                    entry_index,
                } => {
                    let (ref src_archive_path, ref mut src_archive) =
                        self.src_archives[archive_index];
                    let entry = src_archive.jump_to_entry(entry_index).unwrap();
                    let header = entry.header().clone();

                    match builder {
                        BuilderKind::Bsd(ref mut builder) => {
                            builder.append(&header, entry).unwrap()
                        }
                        BuilderKind::Gnu(ref mut builder) => {
                            builder.append(&header, entry).unwrap()
                        }
                        BuilderKind::NativeAr(archive_file) => {
                            Command::new("ar")
                                .arg("x")
                                .arg(src_archive_path)
                                .arg(&entry_name)
                                .status()
                                .unwrap();
                            add_file_using_ar(archive_file, Path::new(&entry_name));
                            std::fs::remove_file(entry_name).unwrap();
                        }
                    }
                }
                ArchiveEntry::File(file) => match builder {
                    BuilderKind::Bsd(ref mut builder) => builder
                        .append_file(entry_name.as_bytes(), &mut File::open(file).unwrap())
                        .unwrap(),
                    BuilderKind::Gnu(ref mut builder) => builder
                        .append_file(entry_name.as_bytes(), &mut File::open(file).unwrap())
                        .unwrap(),
                    BuilderKind::NativeAr(archive_file) => add_file_using_ar(archive_file, &file),
                },
            }
        }

        // Finalize archive
        std::mem::drop(builder);

        // Run ranlib to be able to link the archive
        let status = std::process::Command::new("ranlib")
            .arg(self.config.dst)
            .status()
            .expect("Couldn't run ranlib");

        if !status.success() {
            self.config.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
        }
    }
}

impl<'a> ArArchiveBuilder<'a> {
    fn add_archive<F>(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()>
    where
        F: FnMut(&str) -> bool + 'static,
    {
        let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
        let archive_index = self.src_archives.len();

        let mut i = 0;
        while let Some(entry) = archive.next_entry() {
            let entry = entry.unwrap();
            let file_name = String::from_utf8(entry.header().identifier().to_vec()).unwrap();
            if !skip(&file_name) {
                self.entries.push((
                    file_name,
                    ArchiveEntry::FromArchive {
                        archive_index,
                        entry_index: i,
                    },
                ));
            }
            i += 1;
        }

        self.src_archives.push((archive_path, archive));
        Ok(())
    }
}
<commit_msg>Only run ranlib when necessary<commit_after>use std::fs::File;
use std::path::{Path, PathBuf};

use crate::prelude::*;

use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
use rustc_codegen_ssa::METADATA_FILENAME;

struct ArchiveConfig<'a> {
    sess: &'a Session,
    dst: PathBuf,
    lib_search_paths: Vec<PathBuf>,
    use_native_ar: bool,
    use_gnu_style_archive: bool,
}

#[derive(Debug)]
enum ArchiveEntry {
    FromArchive {
        archive_index: usize,
        entry_index: usize,
    },
    File(PathBuf),
}

pub(crate) struct ArArchiveBuilder<'a> {
    config: ArchiveConfig<'a>,
    src_archives: Vec<(PathBuf, ar::Archive<File>)>,
    // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
    // the end of an archive for linkers to not get confused.
    entries: Vec<(String, ArchiveEntry)>,
    update_symbols: bool,
}

impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
    fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
        use rustc_codegen_ssa::back::link::archive_search_paths;
        let config = ArchiveConfig {
            sess,
            dst: output.to_path_buf(),
            lib_search_paths: archive_search_paths(sess),
            use_native_ar: false,
            // FIXME test for linux and System V derivatives instead
            use_gnu_style_archive: sess.target.target.options.archive_format == "gnu",
        };

        let (src_archives, entries) = if let Some(input) = input {
            let mut archive = ar::Archive::new(File::open(input).unwrap());
            let mut entries = Vec::new();

            let mut i = 0;
            while let Some(entry) = archive.next_entry() {
                let entry = entry.unwrap();
                entries.push((
                    String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
                    ArchiveEntry::FromArchive {
                        archive_index: 0,
                        entry_index: i,
                    },
                ));
                i += 1;
            }

            (vec![(input.to_owned(), archive)], entries)
        } else {
            (vec![], Vec::new())
        };

        ArArchiveBuilder {
            config,
            src_archives,
            entries,
            update_symbols: false,
        }
    }

    fn src_files(&mut self) -> Vec<String> {
        self.entries.iter().map(|(name, _)| name.clone()).collect()
    }

    fn remove_file(&mut self, name: &str) {
        let index = self
            .entries
            .iter()
            .position(|(entry_name, _)| entry_name == name)
            .expect("Tried to remove file not existing in src archive");
        self.entries.remove(index);
    }

    fn add_file(&mut self, file: &Path) {
        self.entries.push((
            file.file_name().unwrap().to_str().unwrap().to_string(),
            ArchiveEntry::File(file.to_owned()),
        ));
    }

    fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) {
        let location = find_library(name, &self.config.lib_search_paths, self.config.sess);
        self.add_archive(location.clone(), |_| false)
            .unwrap_or_else(|e| {
                panic!(
                    "failed to add native library {}: {}",
                    location.to_string_lossy(),
                    e
                );
            });
    }

    fn add_rlib(
        &mut self,
        rlib: &Path,
        name: &str,
        lto: bool,
        skip_objects: bool,
    ) -> std::io::Result<()> {
        let obj_start = name.to_owned();

        self.add_archive(rlib.to_owned(), move |fname: &str| {
            // Ignore metadata files, no matter the name.
            if fname == METADATA_FILENAME {
                return true;
            }

            // Don't include Rust objects if LTO is enabled
            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
                return true;
            }

            // Otherwise if this is *not* a rust object and we're skipping
            // objects then skip this file
            if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
                return true;
            }

            // ok, don't skip this
            return false;
        })
    }

    fn update_symbols(&mut self) {
        self.update_symbols = true;
    }

    fn build(mut self) {
        use std::process::Command;

        fn add_file_using_ar(archive: &Path, file: &Path) {
            Command::new("ar")
                .arg("r") // add or replace file
                .arg("-c") // silence created file message
                .arg(archive)
                .arg(&file)
                .status()
                .unwrap();
        }

        enum BuilderKind<'a> {
            Bsd(ar::Builder<File>),
            Gnu(ar::GnuBuilder<File>),
            NativeAr(&'a Path),
        }

        let mut builder = if self.config.use_native_ar {
            BuilderKind::NativeAr(&self.config.dst)
        } else if self.config.use_gnu_style_archive {
            BuilderKind::Gnu(ar::GnuBuilder::new(
                File::create(&self.config.dst).unwrap(),
                self.entries
                    .iter()
                    .map(|(name, _)| name.as_bytes().to_vec())
                    .collect(),
            ))
        } else {
            BuilderKind::Bsd(ar::Builder::new(File::create(&self.config.dst).unwrap()))
        };

        // Add all files
        for (entry_name, entry) in self.entries.into_iter() {
            match entry {
                ArchiveEntry::FromArchive {
                    archive_index,
                    entry_index,
                } => {
                    let (ref src_archive_path, ref mut src_archive) =
                        self.src_archives[archive_index];
                    let entry = src_archive.jump_to_entry(entry_index).unwrap();
                    let header = entry.header().clone();

                    match builder {
                        BuilderKind::Bsd(ref mut builder) => {
                            builder.append(&header, entry).unwrap()
                        }
                        BuilderKind::Gnu(ref mut builder) => {
                            builder.append(&header, entry).unwrap()
                        }
                        BuilderKind::NativeAr(archive_file) => {
                            Command::new("ar")
                                .arg("x")
                                .arg(src_archive_path)
                                .arg(&entry_name)
                                .status()
                                .unwrap();
                            add_file_using_ar(archive_file, Path::new(&entry_name));
                            std::fs::remove_file(entry_name).unwrap();
                        }
                    }
                }
                ArchiveEntry::File(file) => match builder {
                    BuilderKind::Bsd(ref mut builder) => builder
                        .append_file(entry_name.as_bytes(), &mut File::open(file).unwrap())
                        .unwrap(),
                    BuilderKind::Gnu(ref mut builder) => builder
                        .append_file(entry_name.as_bytes(), &mut File::open(file).unwrap())
                        .unwrap(),
                    BuilderKind::NativeAr(archive_file) => add_file_using_ar(archive_file, &file),
                },
            }
        }

        // Finalize archive
        std::mem::drop(builder);

        if self.update_symbols {
            // Run ranlib to be able to link the archive
            let status = std::process::Command::new("ranlib")
                .arg(self.config.dst)
                .status()
                .expect("Couldn't run ranlib");

            if !status.success() {
                self.config.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
            }
        }
    }
}

impl<'a> ArArchiveBuilder<'a> {
    fn add_archive<F>(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()>
    where
        F: FnMut(&str) -> bool + 'static,
    {
        let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
        let archive_index = self.src_archives.len();

        let mut i = 0;
        while let Some(entry) = archive.next_entry() {
            let entry = entry.unwrap();
            let file_name = String::from_utf8(entry.header().identifier().to_vec()).unwrap();
            if !skip(&file_name) {
                self.entries.push((
                    file_name,
                    ArchiveEntry::FromArchive {
                        archive_index,
                        entry_index: i,
                    },
                ));
            }
            i += 1;
        }

        self.src_archives.push((archive_path, archive));
        Ok(())
    }
}
<|endoftext|>


<commit_before>
use std::ffi;
use std::ffi::AsOsStr;
use std::convert::AsRef;
use std::path::Path;

pub mod record;
pub mod header;

use htslib;
use bcf::header::HeaderView;
pub use bcf::header::Header;

pub struct Reader {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Reader {
   pub fn new<P: AsRef<Path>>(path: &P) -> Self {
        let htsfile = bcf_open(path, b"r");
        let header = unsafe { htslib::vcf::bcf_hdr_read(htsfile) };
        Reader { inner: htsfile, header: HeaderView::new(header) }
    }

    pub fn read(&self, record: &mut record::Record) -> Result<(), ReadError> {
        match unsafe { htslib::vcf::bcf_read(self.inner, self.header.inner, record.inner) } {
            0  => {
                record.header = self.header.inner;
                Ok(())
            },
            -1 => Err(ReadError::NoMoreRecord),
            _  => Err(ReadError::Invalid),
        }
    }

    pub fn records(&self) -> Records {
        Records { reader: self }
    }
}

impl Drop for Reader {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Writer {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Writer {
    pub fn new<P: AsRef<Path>>(path: &P, header: &Header) -> Self {
        let htsfile = bcf_open(path, b"w");
        unsafe { htslib::vcf::bcf_hdr_write(htsfile, header.inner) };
        Writer { inner: htsfile, header: HeaderView::new(unsafe { htslib::vcf::bcf_hdr_dup(header.inner) }) }
    }

    pub fn write(&mut self, record: &record::Record) -> Result<(), ()> {
        if unsafe { htslib::vcf::bcf_write(self.inner, self.header.inner, record.inner) } == -1 {
            Err(())
        }
        else {
            Ok(())
        }
    }
}

impl Drop for Writer {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Records<'a> {
    reader: &'a Reader,
}

impl<'a> Iterator for Records<'a> {
    type Item = Result<record::Record, ReadError>;

    fn next(&mut self) -> Option<Result<record::Record, ReadError>> {
        let mut record = record::Record::new();
        match self.reader.read(&mut record) {
            Err(ReadError::NoMoreRecord) => None,
            Err(e)                       => Some(Err(e)),
            Ok(())                       => Some(Ok(record)),
        }
    }
}

/// Wrapper for opening a BCF file.
fn bcf_open<P: AsRef<Path>>(path: &P, mode: &[u8]) -> *mut htslib::vcf::htsFile {
    unsafe {
        htslib::vcf::hts_open(
            path.as_ref().as_os_str().to_cstring().unwrap().as_ptr(),
            ffi::CString::new(mode).unwrap().as_ptr()
        )
    }
}

pub enum ReadError {
    Truncated,
    Invalid,
    NoMoreRecord,
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use std::path::Path;

    fn _test_read<P: AsRef<Path>>(path: &P) {
        let bcf = Reader::new(path);
        assert_eq!(bcf.header.samples(), [b"NA12878.subsample-0.25-0"]);

        for (i, rec) in bcf.records().enumerate() {
            let mut record = rec.ok().expect("Error reading record.");

            assert_eq!(record.rid().expect("Error reading rid."), 0);
            assert_eq!(record.pos(), 10021 + i as u32);
            assert_eq!(record.qual(), 0f32);
            assert_eq!(record.info(b"MQ0F").float().ok().expect("Error reading info."), [1.0]);
            if i == 59 {
                assert_eq!(record.info(b"SGB").float().ok().expect("Error reading info."), [-0.379885]);
            }
            // the artificial "not observed" allele is present in each record.
            assert_eq!(record.alleles().iter().last().unwrap(), b"<X>");

            let mut fmt = record.format(b"PL");
            let pl = fmt.integer().ok().expect("Error reading format.");
            assert_eq!(pl.len(), 1);
            if i == 59 {
                assert_eq!(pl[0].len(), 6);
            }
            else {
                assert_eq!(pl[0].len(), 3);
            }
        }
    }

    #[test]
    fn test_read() {
        _test_read(&"test.bcf");
    }

    #[test]
    fn test_write() {
        let bcf = Reader::new(&"test.bcf");
        let tmp = tempdir::TempDir::new("rust-htslib").ok().expect("Cannot create temp dir");
        let bcfpath = tmp.path().join("test.bcf");
        println!("{:?}", bcfpath);
        {
            let header = Header::with_template(&bcf.header);
            let mut writer = Writer::new(&bcfpath, &header);
            for record in bcf.records() {
                writer.write(&record.ok().expect("Error reading record.")).ok().expect("Error writing record");
            }
        }
        {
            _test_read(&bcfpath);
        }
        tmp.close().ok().expect("Failed to delete temp dir");
    }
}
<commit_msg>Publish Record.<commit_after>
use std::ffi;
use std::ffi::AsOsStr;
use std::convert::AsRef;
use std::path::Path;

pub mod record;
pub mod header;

use htslib;
use bcf::header::HeaderView;

pub use bcf::header::Header;
pub use bcf::record::Record;

pub struct Reader {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Reader {
   pub fn new<P: AsRef<Path>>(path: &P) -> Self {
        let htsfile = bcf_open(path, b"r");
        let header = unsafe { htslib::vcf::bcf_hdr_read(htsfile) };
        Reader { inner: htsfile, header: HeaderView::new(header) }
    }

    pub fn read(&self, record: &mut record::Record) -> Result<(), ReadError> {
        match unsafe { htslib::vcf::bcf_read(self.inner, self.header.inner, record.inner) } {
            0  => {
                record.header = self.header.inner;
                Ok(())
            },
            -1 => Err(ReadError::NoMoreRecord),
            _  => Err(ReadError::Invalid),
        }
    }

    pub fn records(&self) -> Records {
        Records { reader: self }
    }
}

impl Drop for Reader {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Writer {
    inner: *mut htslib::vcf::htsFile,
    pub header: HeaderView,
}

impl Writer {
    pub fn new<P: AsRef<Path>>(path: &P, header: &Header) -> Self {
        let htsfile = bcf_open(path, b"w");
        unsafe { htslib::vcf::bcf_hdr_write(htsfile, header.inner) };
        Writer { inner: htsfile, header: HeaderView::new(unsafe { htslib::vcf::bcf_hdr_dup(header.inner) }) }
    }

    pub fn write(&mut self, record: &record::Record) -> Result<(), ()> {
        if unsafe { htslib::vcf::bcf_write(self.inner, self.header.inner, record.inner) } == -1 {
            Err(())
        }
        else {
            Ok(())
        }
    }
}

impl Drop for Writer {
    fn drop(&mut self) {
        unsafe {
            htslib::vcf::bcf_hdr_destroy(self.header.inner);
            htslib::vcf::hts_close(self.inner);
        }
    }
}

pub struct Records<'a> {
    reader: &'a Reader,
}

impl<'a> Iterator for Records<'a> {
    type Item = Result<record::Record, ReadError>;

    fn next(&mut self) -> Option<Result<record::Record, ReadError>> {
        let mut record = record::Record::new();
        match self.reader.read(&mut record) {
            Err(ReadError::NoMoreRecord) => None,
            Err(e)                       => Some(Err(e)),
            Ok(())                       => Some(Ok(record)),
        }
    }
}

/// Wrapper for opening a BCF file.
fn bcf_open<P: AsRef<Path>>(path: &P, mode: &[u8]) -> *mut htslib::vcf::htsFile {
    unsafe {
        htslib::vcf::hts_open(
            path.as_ref().as_os_str().to_cstring().unwrap().as_ptr(),
            ffi::CString::new(mode).unwrap().as_ptr()
        )
    }
}

pub enum ReadError {
    Truncated,
    Invalid,
    NoMoreRecord,
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use std::path::Path;

    fn _test_read<P: AsRef<Path>>(path: &P) {
        let bcf = Reader::new(path);
        assert_eq!(bcf.header.samples(), [b"NA12878.subsample-0.25-0"]);

        for (i, rec) in bcf.records().enumerate() {
            let mut record = rec.ok().expect("Error reading record.");

            assert_eq!(record.rid().expect("Error reading rid."), 0);
            assert_eq!(record.pos(), 10021 + i as u32);
            assert_eq!(record.qual(), 0f32);
            assert_eq!(record.info(b"MQ0F").float().ok().expect("Error reading info."), [1.0]);
            if i == 59 {
                assert_eq!(record.info(b"SGB").float().ok().expect("Error reading info."), [-0.379885]);
            }
            // the artificial "not observed" allele is present in each record.
            assert_eq!(record.alleles().iter().last().unwrap(), b"<X>");

            let mut fmt = record.format(b"PL");
            let pl = fmt.integer().ok().expect("Error reading format.");
            assert_eq!(pl.len(), 1);
            if i == 59 {
                assert_eq!(pl[0].len(), 6);
            }
            else {
                assert_eq!(pl[0].len(), 3);
            }
        }
    }

    #[test]
    fn test_read() {
        _test_read(&"test.bcf");
    }

    #[test]
    fn test_write() {
        let bcf = Reader::new(&"test.bcf");
        let tmp = tempdir::TempDir::new("rust-htslib").ok().expect("Cannot create temp dir");
        let bcfpath = tmp.path().join("test.bcf");
        println!("{:?}", bcfpath);
        {
            let header = Header::with_template(&bcf.header);
            let mut writer = Writer::new(&bcfpath, &header);
            for record in bcf.records() {
                writer.write(&record.ok().expect("Error reading record.")).ok().expect("Error writing record");
            }
        }
        {
            _test_read(&bcfpath);
        }
        tmp.close().ok().expect("Failed to delete temp dir");
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "tools")]

extern crate octavo;
extern crate clap;
extern crate rustc_serialize;

use std::io::{self, Read};
use std::fs::File;

use clap::{App, Arg};
use rustc_serialize::hex::ToHex;

use octavo::digest::*;

const ENGINES: &'static [&'static str] = &[
    "MD4",
    "MD5",
    "RIPEMD-160",
    "SHA1",
    "SHA-224",
    "SHA-256",
    "SHA-384",
    "SHA-512",
    "SHA3-224",
    "SHA3-256",
    "SHA3-384",
    "SHA3-512"
];

fn checksum<Eng: Digest + Default>(input: &mut Read, buffer_size: usize) -> String {
    let mut eng = Eng::default();
    let mut buf = vec![0; buffer_size];

    while let Ok(len) = input.read(&mut buf[..]) {
        if len == 0 { break }

        eng.update(&buf[..len]);
    }

    let mut result = vec![0; Eng::output_bytes()];
    eng.result(&mut result[..]);

    result[..].to_hex()
}

macro_rules! digest_for {
    (match $name:ident with $size:expr, $input:ident { $($val:pat => $eng:ty,)+ }) => {
        match $name {
            $($val => checksum::<$eng>(&mut $input, $size),)*
                _ => unreachable!()
        }
    }
}

fn input(name: &str) -> io::Result<Box<Read>> {
    if name == "-" { return Ok(Box::new(io::stdin())); }

    Ok(Box::new(try!(File::open(name))))
}

fn main() {
    let matches = App::new("sum")
        .arg(Arg::with_name("digest")
             .help("Select checksum engine. Defaults to SHA1.")
             .possible_values(ENGINES)
             .short("D")
             .long("digest")
             .global(true)
             .empty_values(false)
             .takes_value(true))
        .arg(Arg::with_name("FILE")
             .help("Files to compute checksums. `-` means STDIN. Defaults to STDIN.")
             .multiple(true))
        .arg(Arg::with_name("buffer size")
             .help("Size of read buffer in bytes. Defaults to 1MiB.")
             .takes_value(true)
             .long("buffer-size"))
        .get_matches();

    let engine_name = matches.value_of("digest").unwrap_or("SHA1");
    let files = matches.values_of("FILE").unwrap_or(vec!["-"]);
    let buffer_size = matches.value_of("buffer size")
        .and_then(|val| val.parse().ok())
        .unwrap_or(1024 * 1024);

    for file in files {
        let mut input: Box<Read> = input(file).unwrap();

        let result = digest_for! {
            match engine_name with buffer_size, input {
                "MD4" => md4::Md4,
                "MD5" => md5::Md5,
                "RIPEMD-160" => ripemd::Ripemd160,
                "SHA1" => sha1::Sha1,
                "SHA-224" => sha2::Sha224,
                "SHA-256" => sha2::Sha256,
                "SHA-384" => sha2::Sha384,
                "SHA-512" => sha2::Sha512,
                "SHA3-224" => sha3::Sha3224,
                "SHA3-256" => sha3::Sha3256,
                "SHA3-384" => sha3::Sha3384,
                "SHA3-512" => sha3::Sha3512,
            }
        };

        println!("{} {}", result, file);
    }
}
<commit_msg>feat(sum): add Tiger supported hashes<commit_after>#![cfg(feature = "tools")]

extern crate octavo;
extern crate clap;
extern crate rustc_serialize;

use std::io::{self, Read};
use std::fs::File;

use clap::{App, Arg};
use rustc_serialize::hex::ToHex;

use octavo::digest::*;

const ENGINES: &'static [&'static str] = &[
    "MD4",
    "MD5",
    "RIPEMD-160",
    "SHA1",
    "SHA-224",
    "SHA-256",
    "SHA-384",
    "SHA-512",
    "SHA3-224",
    "SHA3-256",
    "SHA3-384",
    "SHA3-512",
    "Tiger"
];

fn checksum<Eng: Digest + Default>(input: &mut Read, buffer_size: usize) -> String {
    let mut eng = Eng::default();
    let mut buf = vec![0; buffer_size];

    while let Ok(len) = input.read(&mut buf[..]) {
        if len == 0 { break }

        eng.update(&buf[..len]);
    }

    let mut result = vec![0; Eng::output_bytes()];
    eng.result(&mut result[..]);

    result[..].to_hex()
}

macro_rules! digest_for {
    (match $name:ident with $size:expr, $input:ident { $($val:pat => $eng:ty,)+ }) => {
        match $name {
            $($val => checksum::<$eng>(&mut $input, $size),)*
                _ => unreachable!()
        }
    }
}

fn input(name: &str) -> io::Result<Box<Read>> {
    if name == "-" { return Ok(Box::new(io::stdin())); }

    Ok(Box::new(try!(File::open(name))))
}

fn main() {
    let matches = App::new("sum")
        .arg(Arg::with_name("digest")
             .help("Select checksum engine. Defaults to SHA1.")
             .possible_values(ENGINES)
             .short("D")
             .long("digest")
             .global(true)
             .empty_values(false)
             .takes_value(true))
        .arg(Arg::with_name("FILE")
             .help("Files to compute checksums. `-` means STDIN. Defaults to STDIN.")
             .multiple(true))
        .arg(Arg::with_name("buffer size")
             .help("Size of read buffer in bytes. Defaults to 1MiB.")
             .takes_value(true)
             .long("buffer-size"))
        .get_matches();

    let engine_name = matches.value_of("digest").unwrap_or("SHA1");
    let files = matches.values_of("FILE").unwrap_or(vec!["-"]);
    let buffer_size = matches.value_of("buffer size")
        .and_then(|val| val.parse().ok())
        .unwrap_or(1024 * 1024);

    for file in files {
        let mut input: Box<Read> = input(file).unwrap();

        let result = digest_for! {
            match engine_name with buffer_size, input {
                "MD4" => md4::Md4,
                "MD5" => md5::Md5,
                "RIPEMD-160" => ripemd::Ripemd160,
                "SHA1" => sha1::Sha1,
                "SHA-224" => sha2::Sha224,
                "SHA-256" => sha2::Sha256,
                "SHA-384" => sha2::Sha384,
                "SHA-512" => sha2::Sha512,
                "SHA3-224" => sha3::Sha3224,
                "SHA3-256" => sha3::Sha3256,
                "SHA3-384" => sha3::Sha3384,
                "SHA3-512" => sha3::Sha3512,
                "Tiger" => tiger::Tiger
            }
        };

        println!("{} {}", result, file);
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::iter::FromIterator;
use std::ops::{
    Range,
    RangeFrom,
    RangeFull,
    RangeTo,
};

use {Input, ParseResult};
use primitives::{InputClone, InputBuffer, IntoInner, State};

/// Internal macro to provide speedup for `FromIterator::from_iter`.
///
/// Needed as of rustc 1.7.0-nightly (81ae8be71 2015-12-09).
macro_rules! run_from_iter(
    ( $iter:ident as $r_ty:ty ) => {
        {
            // Dummy variable to tie the scope of the iteration into the current scope,
            // this causes rustc to inline `Iterator::next` into this scope including
            // `FromIterator::from_iter`.
            // This is probably how it works, #[inline] and #[inline(always)] do not affect it as
            // of rustc 1.7.0-nightly (81ae8be71 2015-12-09).
            let mut item = false;

            // the inspect() is useless here, but ties the inner scope of the loop to the current
            // scope which will make it inline `Iterator::next`.
            let result: T = FromIterator::from_iter($iter.by_ref().inspect(|_| item = true));

            // Oddly enough, no inspect() is much much slower:
            //
            // ```
            // let result: $r_ty = FromIterator::from_iter($iter.by_ref());
            // ```
            //
            // The above code is only faster in very very small benchmarks, anything larger than
            // the benchmarks for many (ie. multiple `many(i, any)`) will most likely be slower
            // when using the above line.

            // The performance difference is definitely noticeable in benchmarks, parsing 10k bytes of
            // any type and just storing them in a Vec is 10x slower on current nightly
            // (rustc 1.7.0-nightly (81ae8be71 2015-12-09)).

            (result, $iter.end_state())
        }
    }
);

/// Macro to implement a parser iterator, it provides the ability to add an extra state variable
/// into it and also provide a size_hint as well as a pre- and on-next hooks.
macro_rules! impl_iter {
    ( $iter_name:ident ( $data_ty:ty ) {
        size_hint($size_hint_self:ident) $size_hint:block

        next($next_self:ident) {
            pre $pre_next:block
            on  $on_next:block
        }
    } ) => {
        enum EndState<'a, I, E>
          where I: 'a {
            Error(&'a [I], E),
            Incomplete(usize),
        }

        struct $iter_name<'a, I, T, E, F>
          where I: 'a,
                T: 'a,
                E: 'a,
                F: FnMut(Input<'a, I>) -> ParseResult<'a, I, T, E> {
            /// Last state of the parser
            state:  EndState<'a, I, E>,
            /// Parser to execute once for each iteration
            parser: F,
            /// Remaining buffer
            buf:    Input<'a, I>,
            /// Nested state
            data:   $data_ty,
            _t:     PhantomData<T>,
        }

        impl<'a, I, T, E, F> $iter_name<'a, I, T, E, F>
          where I: 'a,
                T: 'a,
                E: 'a,
                F: FnMut(Input<'a, I>) -> ParseResult<'a, I, T, E> {
            #[inline(always)]
            fn end_state(self) -> (Input<'a, I>, $data_ty, EndState<'a, I, E>) {
                (self.buf, self.data, self.state)
            }
        }

        impl<'a, I, T, E, F> Iterator for $iter_name<'a, I, T, E, F>
          where I: 'a,
                T: 'a,
                E: 'a,
                F: FnMut(Input<'a, I>) -> ParseResult<'a, I, T, E> {
            type Item = T;

            #[inline(always)]
            fn size_hint(&$size_hint_self) -> (usize, Option<usize>) {
                $size_hint
            }

            #[inline(always)]
            fn next(&mut $next_self) -> Option<Self::Item> {
                $pre_next

                match ($next_self.parser)($next_self.buf.clone()).into_inner() {
                    State::Data(b, v) => {
                        $next_self.buf    = b;

                        $on_next

                        Some(v)
                    },
                    State::Error(b, e) => {
                        $next_self.state = EndState::Error(b, e);

                        None
                    },
                    State::Incomplete(n) => {
                        $next_self.state = EndState::Incomplete(n);

                        None
                    },
                }
            }
        }
    };
    ( $iter_name:ident ( $data_ty:ty ) {
        size_hint($size_hint_self:ident) $size_hint:block
    } ) => {
        impl_iter!($iter_name ( $data_ty ) {
            size_hint ( $size_hint_self ) $size_hint

            next(self) {
                pre {}
                on  {}
            }
        })
    };
}

/// Trait for iterating a parser based on a range.
pub trait BoundedRange {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U>;
}

impl BoundedRange for Range<usize> {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(ToIter((usize, usize)) {
            size_hint(self) {
                (self.data.0, Some(self.data.1))
            }

            next(self) {
                pre {
                    if self.data.1 == 0 {
                        return None;
                    }
                }
                on {
                    self.data.0  = if self.data.0 == 0 { 0 } else { self.data.0 - 1 };
                    self.data.1 -= 1;
                }
            }
        });

        let mut iter = ToIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            data:   (self.start, self.end),
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            // Got all occurrences of the parser
            (s, (0, 0), _) => s.ret(result),
            // Ok, last parser failed and we have reached minimum, we have iterated all.
            // Return remainder of buffer and the collected result
            (s, (0, _), EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete but reached at least minimum, propagate if not at end
            (s, (0, _), EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
            // Did not reach minimum, propagate
            (s, (_, _), EndState::Error(b, e))   => s.replace(b).err(e),
            (s, (_, _), EndState::Incomplete(n)) => s.incomplete(n),
        }
    }
}

impl BoundedRange for RangeFrom<usize> {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(FromIter(usize) {
            size_hint(self) {
                (self.data, None)
            }

            next(self) {
                pre {}
                on  {
                    self.data = if self.data == 0 { 0 } else { self.data - 1 };
                }
            }
        });

        let mut iter = FromIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            data:   self.start,
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            // We got at least n items
            (s, 0, EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete, propagate if not at end
            (s, 0, EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
            // Items still remaining, propagate
            (s, _, EndState::Error(b, e))   => s.replace(b).err(e),
            (s, _, EndState::Incomplete(n)) => s.incomplete(n),
        }
    }
}

impl BoundedRange for RangeFull {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(ManyIter(()) {
            size_hint(self) {
                (0, None)
            }
        });

        let mut iter = ManyIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            data:   (),
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            (s, (), EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete, propagate if not at end
            (s, (), EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
        }
    }
}

impl BoundedRange for RangeTo<usize> {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(ToIter(usize) {
            size_hint(self) {
                (0, Some(self.data))
            }

            next(self) {
                pre {
                    if self.data == 0 {
                        return None;
                    }
                }
                on {
                    self.data  -= 1;
                }
            }
        });

        let mut iter = ToIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            data:   self.end,
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            // Either error or incomplete after the end
            (s, 0, _)                       => s.ret(result),
            // Inside of range, never outside
            (s, _, EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete, propagate if not at end
            (s, _, EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
        }
    }
}

#[inline]
pub fn many<'a, I, T, E, F, U, R>(i: Input<'a, I>, r: R, f: F) -> ParseResult<'a, I, T, E>
  where I: Copy,
        R: BoundedRange,
        U: 'a,
        F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
        T: FromIterator<U> {
    BoundedRange::parse(&r, i, f)
}
<commit_msg>Bounded: Ranges are exclusive end<commit_after>use std::marker::PhantomData;
use std::iter::FromIterator;
use std::ops::{
    Range,
    RangeFrom,
    RangeFull,
    RangeTo,
};
use std::cmp::max;

use {Input, ParseResult};
use primitives::{InputClone, InputBuffer, IntoInner, State};

/// Internal macro to provide speedup for `FromIterator::from_iter`.
///
/// Needed as of rustc 1.7.0-nightly (81ae8be71 2015-12-09).
macro_rules! run_from_iter(
    ( $iter:ident as $r_ty:ty ) => {
        {
            // Dummy variable to tie the scope of the iteration into the current scope,
            // this causes rustc to inline `Iterator::next` into this scope including
            // `FromIterator::from_iter`.
            // This is probably how it works, #[inline] and #[inline(always)] do not affect it as
            // of rustc 1.7.0-nightly (81ae8be71 2015-12-09).
            let mut item = false;

            // the inspect() is useless here, but ties the inner scope of the loop to the current
            // scope which will make it inline `Iterator::next`.
            let result: T = FromIterator::from_iter($iter.by_ref().inspect(|_| item = true));

            // Oddly enough, no inspect() is much much slower:
            //
            // ```
            // let result: $r_ty = FromIterator::from_iter($iter.by_ref());
            // ```
            //
            // The above code is only faster in very very small benchmarks, anything larger than
            // the benchmarks for many (ie. multiple `many(i, any)`) will most likely be slower
            // when using the above line.

            // The performance difference is definitely noticeable in benchmarks, parsing 10k bytes of
            // any type and just storing them in a Vec is 10x slower on current nightly
            // (rustc 1.7.0-nightly (81ae8be71 2015-12-09)).

            (result, $iter.end_state())
        }
    }
);

/// Macro to implement a parser iterator, it provides the ability to add an extra state variable
/// into it and also provide a size_hint as well as a pre- and on-next hooks.
macro_rules! impl_iter {
    ( $iter_name:ident ( $data_ty:ty ) {
        size_hint($size_hint_self:ident) $size_hint:block

        next($next_self:ident) {
            pre $pre_next:block
            on  $on_next:block
        }
    } ) => {
        enum EndState<'a, I, E>
          where I: 'a {
            Error(&'a [I], E),
            Incomplete(usize),
        }

        struct $iter_name<'a, I, T, E, F>
          where I: 'a,
                T: 'a,
                E: 'a,
                F: FnMut(Input<'a, I>) -> ParseResult<'a, I, T, E> {
            /// Last state of the parser
            state:  EndState<'a, I, E>,
            /// Parser to execute once for each iteration
            parser: F,
            /// Remaining buffer
            buf:    Input<'a, I>,
            /// Nested state
            data:   $data_ty,
            _t:     PhantomData<T>,
        }

        impl<'a, I, T, E, F> $iter_name<'a, I, T, E, F>
          where I: 'a,
                T: 'a,
                E: 'a,
                F: FnMut(Input<'a, I>) -> ParseResult<'a, I, T, E> {
            #[inline(always)]
            fn end_state(self) -> (Input<'a, I>, $data_ty, EndState<'a, I, E>) {
                (self.buf, self.data, self.state)
            }
        }

        impl<'a, I, T, E, F> Iterator for $iter_name<'a, I, T, E, F>
          where I: 'a,
                T: 'a,
                E: 'a,
                F: FnMut(Input<'a, I>) -> ParseResult<'a, I, T, E> {
            type Item = T;

            #[inline(always)]
            fn size_hint(&$size_hint_self) -> (usize, Option<usize>) {
                $size_hint
            }

            #[inline(always)]
            fn next(&mut $next_self) -> Option<Self::Item> {
                $pre_next

                match ($next_self.parser)($next_self.buf.clone()).into_inner() {
                    State::Data(b, v) => {
                        $next_self.buf    = b;

                        $on_next

                        Some(v)
                    },
                    State::Error(b, e) => {
                        $next_self.state = EndState::Error(b, e);

                        None
                    },
                    State::Incomplete(n) => {
                        $next_self.state = EndState::Incomplete(n);

                        None
                    },
                }
            }
        }
    };
    ( $iter_name:ident ( $data_ty:ty ) {
        size_hint($size_hint_self:ident) $size_hint:block
    } ) => {
        impl_iter!($iter_name ( $data_ty ) {
            size_hint ( $size_hint_self ) $size_hint

            next(self) {
                pre {}
                on  {}
            }
        })
    };
}

/// Trait for iterating a parser based on a range.
pub trait BoundedRange {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U>;
}

impl BoundedRange for Range<usize> {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(ToIter((usize, usize)) {
            size_hint(self) {
                (self.data.0, Some(self.data.1))
            }

            next(self) {
                pre {
                    if self.data.1 == 0 {
                        return None;
                    }
                }
                on {
                    self.data.0  = if self.data.0 == 0 { 0 } else { self.data.0 - 1 };
                    self.data.1 -= 1;
                }
            }
        });

        let mut iter = ToIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            // Range is closed on left side, open on right, ie. [self.start, self.end)
            data:   (self.start, max(self.end, 1) - 1),
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            // Got all occurrences of the parser
            (s, (0, 0), _) => s.ret(result),
            // Ok, last parser failed and we have reached minimum, we have iterated all.
            // Return remainder of buffer and the collected result
            (s, (0, _), EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete but reached at least minimum, propagate if not at end
            (s, (0, _), EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
            // Did not reach minimum, propagate
            (s, (_, _), EndState::Error(b, e))   => s.replace(b).err(e),
            (s, (_, _), EndState::Incomplete(n)) => s.incomplete(n),
        }
    }
}

impl BoundedRange for RangeFrom<usize> {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(FromIter(usize) {
            size_hint(self) {
                (self.data, None)
            }

            next(self) {
                pre {}
                on  {
                    self.data = if self.data == 0 { 0 } else { self.data - 1 };
                }
            }
        });

        let mut iter = FromIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            // Inclusive
            data:   self.start,
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            // We got at least n items
            (s, 0, EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete, propagate if not at end
            (s, 0, EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
            // Items still remaining, propagate
            (s, _, EndState::Error(b, e))   => s.replace(b).err(e),
            (s, _, EndState::Incomplete(n)) => s.incomplete(n),
        }
    }
}

impl BoundedRange for RangeFull {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(ManyIter(()) {
            size_hint(self) {
                (0, None)
            }
        });

        let mut iter = ManyIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            // No data required
            data:   (),
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            (s, (), EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete, propagate if not at end
            (s, (), EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
        }
    }
}

impl BoundedRange for RangeTo<usize> {
    #[inline]
    fn parse<'a, I, T, E, F, U>(&self, i: Input<'a, I>, f: F) -> ParseResult<'a, I, T, E>
      where I: Copy,
            U: 'a,
            F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
            T: FromIterator<U> {
        impl_iter!(ToIter(usize) {
            size_hint(self) {
                (0, Some(self.data))
            }

            next(self) {
                pre {
                    if self.data == 0 {
                        return None;
                    }
                }
                on {
                    self.data  -= 1;
                }
            }
        });

        let mut iter = ToIter {
            state:  EndState::Incomplete(1),
            parser: f,
            buf:    i,
            // Exclusive range [0, end)
            data:   max(self.end, 1) - 1,
            _t:     PhantomData,
        };

        let (result, state) = run_from_iter!(iter as T);

        match state {
            // Either error or incomplete after the end
            (s, 0, _)                       => s.ret(result),
            // Inside of range, never outside
            (s, _, EndState::Error(_, _))   => s.ret(result),
            // Nested parser incomplete, propagate if not at end
            (s, _, EndState::Incomplete(n)) => if s.buffer().len() == 0 && s.is_last_slice() {
                s.ret(result)
            } else {
                s.incomplete(n)
            },
        }
    }
}

#[inline]
pub fn many<'a, I, T, E, F, U, R>(i: Input<'a, I>, r: R, f: F) -> ParseResult<'a, I, T, E>
  where I: Copy,
        R: BoundedRange,
        U: 'a,
        F: FnMut(Input<'a, I>) -> ParseResult<'a, I, U, E>,
        T: FromIterator<U> {
    BoundedRange::parse(&r, i, f)
}
<|endoftext|>


<commit_before>/*
Tristen Horton
tristen@tristenhorton.com
2017-04-03

Module for basic channel data
Its possible to be in multiple channels at once in Twitch, so we want a simple way
to represent multiple channels at once - we'll be using this module and structure
for that.
*/

use message::Message;
use std::str::Split;

pub struct Channel {
	name: String,
	state: ChannelState
}

impl Channel {
	pub fn name(&self) -> &String {
		&self.name
	}

	pub fn state(&self) -> &ChannelState {
		&self.state
	}
}

pub struct ChannelState {

	r9k_mode: bool,
	sub_only: bool,
	slow_mode: bool,
	emote_only: bool,

	broadcaster_lang: String,
}

impl ChannelState {
	pub fn from(irc: String) -> ChannelState {
		let properties = irc.split_whitespace().nth(0);
		let mut r9k: bool = false;
		let mut sub: bool = false;
		let mut slow: bool = false;
		let mut emote: bool = false;
		let mut lang: String = String::from("");
		let mut channel: String = String::from("");

		macro_rules! parse_from_nth {
			($tag:ident, $e:expr) => ($tag.nth($e).unwrap().parse().unwrap());
		}

		for prop in properties.unwrap().split(";") {
			let mut prop_split = prop.split("=");
			match prop_split.nth(0).unwrap() {

				"broadcaster-lang" => lang = prop_split.nth(1).unwrap().to_string(),

				"emote-only" => emote = bool_from_nth_str(&mut prop_split, 1),
				"r9k" => r9k = bool_from_nth_str(&mut prop_split, 1),
				"slow" => slow = bool_from_nth_str(&mut prop_split, 1),
				"subs-only" => sub = bool_from_nth_str(&mut prop_split, 1),

				_ => println!("[twitch-rs] Unnacounted for irc property: {:?}", prop),
			}
		}

		channel = irc.to_string().split("#").nth(1).unwrap().to_string();

		ChannelState {
			r9k_mode: r9k,
			sub_only: sub,
			slow_mode: slow,
			emote_only: emote,
			broadcaster_lang: lang,
		}
	}
}

fn bool_from_nth_str(split: &mut Split<&str>, nth: usize) -> bool {
	split.nth(nth)
		.unwrap_or("false")
		.parse::<bool>()
		.unwrap_or(false)
}
<commit_msg>Cleaned up ChannelState<commit_after>/*
Tristen Horton
tristen@tristenhorton.com
2017-04-03

Module for basic channel data
Its possible to be in multiple channels at once in Twitch, so we want a simple way
to represent multiple channels at once - we'll be using this module and structure
for that.
*/

use message::Message;
use std::str::Split;

pub struct Channel {
	name: String,
	state: ChannelState
}

impl Channel {
	pub fn name(&self) -> &String {
		&self.name
	}

	pub fn state(&self) -> &ChannelState {
		&self.state
	}
}

pub struct ChannelState {

	r9k_mode: bool,
	sub_only: bool,
	slow_mode: bool,
	emote_only: bool,

	broadcaster_lang: String,
}

impl ChannelState {
	pub fn from(irc: String) -> ChannelState {

	// typical irc message:
	// broadcaster-lang=;emote-only=0;r9k=0;slow=0;subs-only=1 :tmi.twitch.tv ROOMSTATE #phxvyper

		let properties = irc.split_whitespace().nth(0);
		let mut r9k = false;
		let mut sub = false;
		let mut slow = false;
		let mut emote = false;
		let mut lang = String::from("");
		let mut channel = String::from("");

		for prop in properties.unwrap_or("").split(";") {
			let mut prop_split = prop.split("=");
			match prop_split.nth(0).unwrap_or("") {

				"broadcaster-lang" => lang = prop_split.nth(1).unwrap_or("").to_string(),

				"emote-only" => emote = bool_from_nth_str(&mut prop_split, 1),
				"r9k" => r9k = bool_from_nth_str(&mut prop_split, 1),
				"slow" => slow = bool_from_nth_str(&mut prop_split, 1),
				"subs-only" => sub = bool_from_nth_str(&mut prop_split, 1),

				_ => println!("[twitch-rs] Unnacounted for irc property: {:?}", prop),
			}
		}

		channel = irc.to_string().split("#").nth(1).unwrap_or("").to_string();

		ChannelState {
			r9k_mode: r9k,
			sub_only: sub,
			slow_mode: slow,
			emote_only: emote,
			broadcaster_lang: lang,
		}
	}
}

fn bool_from_nth_str(split: &mut Split<&str>, nth: usize) -> bool {
	split.nth(nth)
		.unwrap_or("false")
		.parse::<bool>()
		.unwrap_or(false)
}
<|endoftext|>


<commit_before>mod init;
mod build;
mod serve;

pub use self::init::create_new_project;
pub use self::build::build;
pub use self::serve::serve;

use gutenberg::Site;

use console::warn;

fn notify_site_size(site: &Site) {
    println!(
        "-> Creating {} pages ({} orphan) and {} sections",
        site.pages.len(),
        site.get_all_orphan_pages().len(),
        site.sections.len()
    );
}

fn warn_about_ignored_pages(site: &Site) {
    let ignored_pages = site.get_ignored_pages();
    if !ignored_pages.is_empty() {
        warn(&format!(
            "{} page(s) ignored (missing date or order in a sorted section):",
            ignored_pages.len()
        ));
        for path in site.get_ignored_pages() {
            warn(&format!("- {}", path.display()));
        }
    }
}
<commit_msg>Do not count the index as a section when printing<commit_after>mod init;
mod build;
mod serve;

pub use self::init::create_new_project;
pub use self::build::build;
pub use self::serve::serve;

use gutenberg::Site;

use console::warn;

fn notify_site_size(site: &Site) {
    println!(
        "-> Creating {} pages ({} orphan) and {} sections",
        site.pages.len(),
        site.get_all_orphan_pages().len(),
        site.sections.len() - 1, // -1 since we do not the index as a section
    );
}

fn warn_about_ignored_pages(site: &Site) {
    let ignored_pages = site.get_ignored_pages();
    if !ignored_pages.is_empty() {
        warn(&format!(
            "{} page(s) ignored (missing date or order in a sorted section):",
            ignored_pages.len()
        ));
        for path in site.get_ignored_pages() {
            warn(&format!("- {}", path.display()));
        }
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

pub fn parse_command(command_line : String) -> Command {
    let command_line = String::from(command_line.trim());
    debug!("parse incoming text: {}", command_line);
    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line);
    if result.is_some() {
        return result.unwrap();
    }
    Command::BadCommand
}

fn try_parse_at(command_line : &String) -> Option<Command>{
    let time_format = Regex::new(r"[at|в]\s*(?P<day>[\d]*)(?:-(?P<month>[\d]*))?(?:-(?P<year>[\d]*))? (?P<hour>[\d]*).(?P<minute>[\d]*) (?P<main_text>.*)").unwrap();

    let date_captures = time_format.captures(command_line);
    if date_captures.is_none() {
        warn!("parse_command: line {} doesn't match time_format regex", command_line);
        return None;
    }

    let date_captures = date_captures.unwrap();

    let text = date_captures.name("main_text").unwrap().as_str();
    let d = get_datetime_from_capture(&date_captures);

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : d }
        ))
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg_main    = Regex::new(r"(?P<spec>[\d\w]*)(?P<divider> )(?P<main_text>.*)").unwrap();

    let reg_day     = r"(?P<days>[\d]*)[D|d|Д|д]";
    let reg_hour    = r"(?P<hours>[\d]*)[H|h|Ч|ч]";
    let reg_min     = r"(?P<minuts>[\d]*)[M|m|М|м]";
    let reg_sec     = r"(?P<seconds>[\d]*)[S|s|С|с]";

    let caps = reg_main.captures(command_line);
    if caps.is_none() {
        warn!("parse_command: incoming text doesn't match main regex");
        return None;
    }
    let caps = caps.unwrap();
    let spec = caps.name("spec").unwrap().as_str();
    let text = caps.name("main_text").unwrap().as_str();

    let days    = get_first_regex_group_as_u32(reg_day,  spec);
    let hours   = get_first_regex_group_as_u32(reg_hour, spec);
    let minutes = get_first_regex_group_as_u32(reg_min,  spec);
    let seconds = get_first_regex_group_as_u32(reg_sec,  spec);

    if days == 0 && hours == 0 && minutes == 0 && seconds == 0 {
        warn!("parse_command: incoming text doesn't match main regex");
        return None;
    }

    // TODO: should we a better way to do this
    let dt = chrono::Duration::seconds((days as i64) * (60*60*24)
                                    + (hours as i64) * (60*60)
                                    + (minutes as i64) * 60
                                    + (seconds as i64)
                                    );

    let event_time = Utc::now() + dt;

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : event_time }
        ))
}

fn get_first_regex_group_as_u32(reg : &str, text : &str) -> u32{
    let reg = Regex::new(reg).expect("get_first_regex_group_as_u32: wrong regex string");
    let number = match reg.captures(text){
        None => "0",
        Some(d) => d.get(1)
            .expect("get_first_regex_group_as_u32: expect regex string with at least 1 group")
            .as_str(),
    };
    number.parse().unwrap_or(0)
}

fn get_datetime_from_capture(cap: &Captures) -> DateTime<Utc>{
    let day = cap.name("day").unwrap().as_str().parse().unwrap();
    let hour = cap.name("hour").unwrap().as_str().parse().unwrap();
    let minute = cap.name("minute").unwrap().as_str().parse().unwrap();

    let now = Utc::now();

    let month = match cap.name("month"){
        None => now.month(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    let year = match cap.name("year"){
        None => now.year(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    const DEFAULT_TZ : i64 = -3;
    let dt = chrono::Duration::seconds(DEFAULT_TZ * 60 * 60);

    Utc.ymd(year, month, day).and_hms(hour, minute, 0) + dt
}
<commit_msg>Small bug fix + add command tests<commit_after>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

// const MOMENT_REGEX: &str =
//     r"(?P<day>[\d]*)(?:-(?P<month>[\d]*))?(?:-(?P<year>[\d]*))? (?P<hour>[\d]*).(?P<minute>[\d]*)";

// const DURATION_REGEX: &str =
//     r"";

pub fn parse_command(command_line : String) -> Command {
    let command_line = String::from(command_line.trim());
    debug!("parse incoming text: {}", command_line);

    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    warn!("parse_command: line {} doesn't match any regex", command_line);
    Command::BadCommand
}

fn try_parse_at(command_line : &String) -> Option<Command>{
    let time_format = Regex::new(r"[at|в]\s*(?P<day>[\d]*)(?:-(?P<month>[\d]*))?(?:-(?P<year>[\d]*))? (?P<hour>[\d]*).(?P<minute>[\d]*) (?P<main_text>.*)").unwrap();

    let date_captures = time_format.captures(command_line);
    if date_captures.is_none() {

        return None;
    }
    let date_captures = date_captures.unwrap();
    let text = date_captures.name("main_text").unwrap().as_str();

    const DEFAULT_TZ : i64 = -3;
    let dt = chrono::Duration::seconds(DEFAULT_TZ * 60 * 60);
    let d = get_datetime_from_capture(&date_captures) + dt;

    Some(Command::OneTimeEvent
            (OneTimeEventImpl
                { event_text : String::from(text)
                , event_time : d }
            ))
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg_main    = Regex::new(r"(?P<spec>[\d\w]*)(?P<divider> )(?P<main_text>.*)").unwrap();

    let reg_day     = r"(?P<days>[\d]*)[D|d|Д|д]";
    let reg_hour    = r"(?P<hours>[\d]*)[H|h|Ч|ч]";
    let reg_min     = r"(?P<minuts>[\d]*)[M|m|М|м]";
    let reg_sec     = r"(?P<seconds>[\d]*)[S|s|С|с]";

    let caps = reg_main.captures(command_line);
    if caps.is_none() {
        return None;
    }
    let caps = caps.unwrap();
    let spec = caps.name("spec").unwrap().as_str();
    let text = caps.name("main_text").unwrap().as_str();

    let days    = get_first_regex_group_as_u32(reg_day,  spec);
    let hours   = get_first_regex_group_as_u32(reg_hour, spec);
    let minutes = get_first_regex_group_as_u32(reg_min,  spec);
    let seconds = get_first_regex_group_as_u32(reg_sec,  spec);

    if days == 0 && hours == 0 && minutes == 0 && seconds == 0 {
        return None;
    }

    // TODO: should we a better way to do this
    let dt = chrono::Duration::seconds((days as i64) * (60*60*24)
                                    + (hours as i64) * (60*60)
                                    + (minutes as i64) * 60
                                    + (seconds as i64)
                                    );

    let event_time = Utc::now() + dt;

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : event_time }
        ))
}

fn get_first_regex_group_as_u32(reg : &str, text : &str) -> u32{
    let reg = Regex::new(reg).expect("get_first_regex_group_as_u32: wrong regex string");
    let number = match reg.captures(text){
        None => "0",
        Some(d) => d.get(1)
            .expect("get_first_regex_group_as_u32: expect regex string with at least 1 group")
            .as_str(),
    };
    number.parse().unwrap_or(0)
}

fn get_datetime_from_capture(cap: &Captures) -> DateTime<Utc>{
    let day = cap.name("day").unwrap().as_str().parse().unwrap();
    let hour = cap.name("hour").unwrap().as_str().parse().unwrap();
    let minute = cap.name("minute").unwrap().as_str().parse().unwrap();

    let now = Utc::now();

    let month = match cap.name("month"){
        None => now.month(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    let year = match cap.name("year"){
        None => now.year(),
        Some(m) => m.as_str().parse().unwrap(),
    };

    Utc.ymd(year, month, day).and_hms(hour, minute, 0)
}

//-------- TESTS ---------------------------------------------------------------

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

    #[test]
    fn parse_for_tests() {
        {
            let command = String::from("1d2h3m4s");
            let text = "some text";
            let command_text = command + " " + text;
            let dt = chrono::Duration::seconds((1 as i64) * (60*60*24)  // days
                                            + (2 as i64) * (60*60)      // hours
                                            + (3 as i64) * 60           // minutes
                                            + (4 as i64)                // seconds
                                            );
            let result = try_parse_for(&command_text);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert!(time_moment_eq(res.event_time, Utc::now() + dt));
                },
                _ => panic!("Wrong command type")
            };

        }
    }

    #[test]
    fn parse_at_tests() {
        {
            let command = String::from("24-10 18:30");
            let text = "some text";
            let mut command_text = command + " " + text;
            command_text.insert_str(0, "at");
            let t = Utc.ymd(2017, 10, 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
    }

    fn time_moment_eq(t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool{
        t1.signed_duration_since(t2).num_milliseconds().abs() < 100
    }
}<|endoftext|>


<commit_before>
use tendril;

use event;
use text;

const ENCODE_CHARS: &[char] = &['&', '<', '>', '"'];

pub(crate) fn encode_char(chr: char) -> &'static str {
    match chr {
        '&' => "&",
        '<' => "<",
        '>' => ">",
        '"' => """,
        c => panic!("unexpected char for encode: '{}'", c.escape_default()),
    }
}

pub(crate) fn encode_str_optional(mut input: &str) -> Option<text::Text> {
    let mut tendril = tendril::StrTendril::new();
    'parts: while !input.is_empty() {
        let len = match input.find(ENCODE_CHARS) {
            Some(pos) => pos,
            None => return None,
        };
        tendril.push_slice(&input[..len]);
        input = &input[len..];
        let chr = match input.chars().next() {
            None => break 'parts,
            Some(chr) => chr,
        };
        tendril.push_slice(encode_char(chr));
        input = &input[chr.len_utf8()..];
    }
    Some(text::Text::from_tendril(tendril))
}

pub(crate) fn encode_str_static(input: &'static str) -> text::Text {
    encode_str_optional(input)
        .unwrap_or_else(|| text::Text::from_static(input))
}

pub(crate) fn encode_str(input: &str) -> text::Text {
    encode_str_optional(input)
        .unwrap_or_else(|| input.into())
}

impl event::IntoStream for String {

    type Stream = Stream;

    fn into_stream(self) -> Self::Stream {
        event::IntoStream::into_stream(&*self)
    }
}

impl<'s> event::IntoStream for &'s str {

    type Stream = Stream;

    fn into_stream(self) -> Self::Stream {
        Stream { data: Some(self.into()) }
    }
}

macro_rules! impl_octal {
    ($name:ty) => {
        impl event::IntoStream for $name {
            type Stream = Stream;
            fn into_stream(self) -> Self::Stream {
                use std::fmt::{ Write };
                let mut tendril = tendril::StrTendril::new();
                write!(tendril, "{:o}", self).expect("writing octal to template");
                Stream { data: Some(text::Data::from_tendril(tendril)) }
            }
        }
    }
}

impl_octal!(usize);
impl_octal!(u8);
impl_octal!(u16);
impl_octal!(u32);
impl_octal!(u64);

impl_octal!(isize);
impl_octal!(i8);
impl_octal!(i16);
impl_octal!(i32);
impl_octal!(i64);

#[derive(Debug)]
pub struct Stream {
    data: Option<text::Data>,
}

impl event::Stream for Stream {

    fn next_event(&mut self) -> event::StreamResult {
        Ok(self.data.take().map(event::data))
    }
}
<commit_msg>added data stream<commit_after>
use tendril;

use event;
use text;

const ENCODE_CHARS: &[char] = &['&', '<', '>', '"'];

pub(crate) fn encode_char(chr: char) -> &'static str {
    match chr {
        '&' => "&",
        '<' => "<",
        '>' => ">",
        '"' => """,
        c => panic!("unexpected char for encode: '{}'", c.escape_default()),
    }
}

pub(crate) fn encode_str_optional(mut input: &str) -> Option<text::Text> {
    let mut tendril = tendril::StrTendril::new();
    'parts: while !input.is_empty() {
        let len = match input.find(ENCODE_CHARS) {
            Some(pos) => pos,
            None => return None,
        };
        tendril.push_slice(&input[..len]);
        input = &input[len..];
        let chr = match input.chars().next() {
            None => break 'parts,
            Some(chr) => chr,
        };
        tendril.push_slice(encode_char(chr));
        input = &input[chr.len_utf8()..];
    }
    Some(text::Text::from_tendril(tendril))
}

pub(crate) fn encode_str_static(input: &'static str) -> text::Text {
    encode_str_optional(input)
        .unwrap_or_else(|| text::Text::from_static(input))
}

pub(crate) fn encode_str(input: &str) -> text::Text {
    encode_str_optional(input)
        .unwrap_or_else(|| input.into())
}

impl event::IntoStream for text::Data {

    type Stream = Stream;

    fn into_stream(self) -> Self::Stream {
        Stream { data: Some(self) }
    }
}

impl event::IntoStream for String {

    type Stream = Stream;

    fn into_stream(self) -> Self::Stream {
        event::IntoStream::into_stream(&*self)
    }
}

impl<'s> event::IntoStream for &'s str {

    type Stream = Stream;

    fn into_stream(self) -> Self::Stream {
        Stream { data: Some(self.into()) }
    }
}

macro_rules! impl_octal {
    ($name:ty) => {
        impl event::IntoStream for $name {
            type Stream = Stream;
            fn into_stream(self) -> Self::Stream {
                use std::fmt::{ Write };
                let mut tendril = tendril::StrTendril::new();
                write!(tendril, "{:o}", self).expect("writing octal to template");
                Stream { data: Some(text::Data::from_tendril(tendril)) }
            }
        }
    }
}

impl_octal!(usize);
impl_octal!(u8);
impl_octal!(u16);
impl_octal!(u32);
impl_octal!(u64);

impl_octal!(isize);
impl_octal!(i8);
impl_octal!(i16);
impl_octal!(i32);
impl_octal!(i64);

#[derive(Debug)]
pub struct Stream {
    data: Option<text::Data>,
}

impl event::Stream for Stream {

    fn next_event(&mut self) -> event::StreamResult {
        Ok(self.data.take().map(event::data))
    }
}
<|endoftext|>