<commit_before>#![deny(warnings)]

extern crate arg_parser;
extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, BufReader, Read, Stderr, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;
use arg_parser::ArgParser;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -vET

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -vE

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -vT

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut parser = ArgParser::new(10).
            add_flag(&["A", "show-all"]). //vET
            add_flag(&["b", "number-nonblank"]).
            add_flag(&["e"]). //vE
            add_flag(&["E", "show-ends"]).
            add_flag(&["n", "number"]).
            add_flag(&["s", "squeeze-blank"]).
            add_flag(&["t"]). //vT
            add_flag(&["T", "show-tabs"]).
            add_flag(&["v", "show-nonprinting"]).
            add_flag(&["h", "help"]);
        parser.parse(env::args());

        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::with_capacity(parser.args.len()),
        };

        if parser.found("help") {
            stdout.write(MAN_PAGE.as_bytes()).try(stderr);
            stdout.flush().try(stderr);
            exit(0);
        }

        if parser.found("show-all") {
            cat.show_nonprinting = true;
            cat.show_ends = true;
            cat.show_tabs = true;
        }

        if parser.found("number") {
            cat.number = true;
            cat.number_nonblank = false;
        }

        if parser.found("number-nonblank") {
            cat.number_nonblank = true;
            cat.number = false;
        }

        if parser.found("show-ends") || parser.found(&'e') {
            cat.show_ends = true;
        }

        if parser.found("squeeze-blank") {
            cat.squeeze_blank = true;
        }

        if parser.found("show-tabs") || parser.found(&'t') {
            cat.show_tabs = true;
        }

        if parser.found("show-nonprinting") || parser.found(&'e') || parser.found(&'t') {
            cat.show_nonprinting = true;
        }

        if !parser.args.is_empty() {
            cat.paths = parser.args;
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let line_count = &mut 0usize;
        let flags_enabled = self.number || self.number_nonblank || self.show_ends || self.show_tabs ||
                            self.squeeze_blank || self.show_nonprinting;

        if self.paths.is_empty() && flags_enabled {
            self.cat(&mut stdin.lock(), line_count, stdout, stderr);
        } else if self.paths.is_empty() {
            io::copy(&mut stdin.lock(), stdout).try(stderr);
        } else {
            for path in &self.paths {
                if flags_enabled && path == "-" {
                    self.cat(&mut stdin.lock(), line_count, stdout, stderr);
                } else if path == "-" {
                    // Copy the standard input directly to the standard output.
                    io::copy(&mut stdin.lock(), stdout).try(stderr);
                } else if fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
                    stderr.write(path.as_bytes()).try(stderr);
                    stderr.write(b": Is a directory\n").try(stderr);
                    stderr.flush().try(stderr);
                    self.exit_status.set(1i32);
                } else if flags_enabled {
                    fs::File::open(&path)
                        // Open the file and copy the file's contents to standard output based input arguments.
                        .map(|file| self.cat(BufReader::new(file), line_count, stdout, stderr))
                        // If an error occurred, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                } else {
                    // Open a file and copy the contents directly to standard output.
                    fs::File::open(&path).map(|ref mut file| { io::copy(file, stdout).try(stderr); })
                        // If an error occurs, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                }
            }
        }
        self.exit_status.get()
    }

    /// Cats either a file or stdin based on the flag arguments given to the program.
    fn cat<F: Read>(&self, file: F, line_count: &mut usize, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in file.bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }
}
/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<commit_msg>Performance improvement for cat (1.97 GB/s to 3.00 GB/s on my machine)<commit_after>#![deny(warnings)]

extern crate arg_parser;
extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, BufReader, Read, Stderr, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;
use arg_parser::ArgParser;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -vET

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -vE

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -vT

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut parser = ArgParser::new(10).
            add_flag(&["A", "show-all"]). //vET
            add_flag(&["b", "number-nonblank"]).
            add_flag(&["e"]). //vE
            add_flag(&["E", "show-ends"]).
            add_flag(&["n", "number"]).
            add_flag(&["s", "squeeze-blank"]).
            add_flag(&["t"]). //vT
            add_flag(&["T", "show-tabs"]).
            add_flag(&["v", "show-nonprinting"]).
            add_flag(&["h", "help"]);
        parser.parse(env::args());

        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::with_capacity(parser.args.len()),
        };

        if parser.found("help") {
            stdout.write(MAN_PAGE.as_bytes()).try(stderr);
            stdout.flush().try(stderr);
            exit(0);
        }

        if parser.found("show-all") {
            cat.show_nonprinting = true;
            cat.show_ends = true;
            cat.show_tabs = true;
        }

        if parser.found("number") {
            cat.number = true;
            cat.number_nonblank = false;
        }

        if parser.found("number-nonblank") {
            cat.number_nonblank = true;
            cat.number = false;
        }

        if parser.found("show-ends") || parser.found(&'e') {
            cat.show_ends = true;
        }

        if parser.found("squeeze-blank") {
            cat.squeeze_blank = true;
        }

        if parser.found("show-tabs") || parser.found(&'t') {
            cat.show_tabs = true;
        }

        if parser.found("show-nonprinting") || parser.found(&'e') || parser.found(&'t') {
            cat.show_nonprinting = true;
        }

        if !parser.args.is_empty() {
            cat.paths = parser.args;
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let line_count = &mut 0usize;
        let flags_enabled = self.number || self.number_nonblank || self.show_ends || self.show_tabs ||
                            self.squeeze_blank || self.show_nonprinting;

        if self.paths.is_empty() && flags_enabled {
            self.cat(&mut stdin.lock(), line_count, stdout, stderr);
        } else if self.paths.is_empty() {
            self.simple_cat(&mut stdin.lock(), stdout, stderr);
        } else {
            for path in &self.paths {
                if flags_enabled && path == "-" {
                    self.cat(&mut stdin.lock(), line_count, stdout, stderr);
                } else if path == "-" {
                    // Copy the standard input directly to the standard output.
                    self.simple_cat(&mut stdin.lock(), stdout, stderr);
                } else if fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
                    stderr.write(path.as_bytes()).try(stderr);
                    stderr.write(b": Is a directory\n").try(stderr);
                    stderr.flush().try(stderr);
                    self.exit_status.set(1i32);
                } else if flags_enabled {
                    fs::File::open(&path)
                        // Open the file and copy the file's contents to standard output based input arguments.
                        .map(|file| self.cat(BufReader::new(file), line_count, stdout, stderr))
                        // If an error occurred, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                } else {
                    // Open a file and copy the contents directly to standard output.
                    fs::File::open(&path).map(|ref mut file| { self.simple_cat(file, stdout, stderr); })
                        // If an error occurs, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                }
            }
        }
        self.exit_status.get()
    }

    /// A simple cat that runs a lot faster than self.cat() due to no iterators over single bytes.
    fn simple_cat<F: Read>(&self, file: &mut F, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut buf: [u8; 8*8192] = [0; 8*8192]; // 64K seems to be the sweet spot for a buffer on my machine.
        loop {
            let n_read = file.read(&mut buf).try(stderr);
            if n_read == 0 { // We've reached the end of the input
                break;
            }
            stdout.write_all(&buf[..n_read]).try(stderr);
        }
    }

    /// Cats either a file or stdin based on the flag arguments given to the program.
    fn cat<F: Read>(&self, file: F, line_count: &mut usize, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in file.bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }
}
/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<|endoftext|>


<commit_before>use clap::{self, Arg, App, SubCommand};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)));
    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }
}
<commit_msg>Add cleanup subcommand<commit_after>use clap::{self, Arg, App, SubCommand};
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use mprovision as mp;

#[derive(Debug, PartialEq)]
pub enum Command {
    List(ListParams),
    Cleanup(Option<PathBuf>),
}

#[derive(Debug, Default, PartialEq)]
pub struct ListParams {
    pub filter: Option<String>,
    pub expires_in_days: Option<i64>,
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

pub fn parse<I, S>(args: I) -> Result
    where I: IntoIterator<Item = S>,
          S: Clone,
          ::std::ffi::OsString: From<S>
{
    let app = App::new("mprovision")
        .about("A tool that helps iOS developers to manage mobileprovision files.")
        .subcommand(SubCommand::with_name("list")
            .arg(Arg::with_name("TEXT")
                .long("--filter")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DAYS")
                .long("--expires-in-days")
                .required(false)
                .empty_values(false)
                .takes_value(true))
            .arg(Arg::with_name("DIRECTORY")
                .required(false)
                .empty_values(false)
                .takes_value(true)))
        .subcommand(SubCommand::with_name("cleanup").arg(Arg::with_name("DIRECTORY")
            .required(false)
            .empty_values(false)
            .takes_value(true)));

    let matches = app.get_matches_from_safe(args)?;
    if let Some(list_matches) = matches.subcommand_matches("list") {
        let mut params = ListParams::default();
        params.filter = list_matches.value_of("TEXT").map(|text| text.to_string());
        if let Some(days) = list_matches.value_of("DAYS")
            .and_then(|days| days.parse::<i64>().ok()) {
            if days < 0 || days > 365 {
                return Err(Error::Custom("DAYS should be between 0 and 365".to_string()));
            }
            params.expires_in_days = Some(days);
        }
        params.directory = list_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::List(params))
    } else if let Some(cleanup_matches) = matches.subcommand_matches("cleanup") {
        let directory = cleanup_matches.value_of("DIRECTORY").map(|dir| dir.into());
        Ok(Command::Cleanup(directory))
    } else {
        Err(Error::Custom("Command isn't implemented".to_string()))
    }
}

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

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "."])).to(be_ok().value(Command::List(ListParams {
            filter: None,
            expires_in_days: None,
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "list", "--filter", "abc"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: None,
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--filter", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expires-in-days", "3"]))
            .to(be_ok().value(Command::List(ListParams {
                filter: None,
                expires_in_days: Some(3),
                directory: None,
            })));

        expect!(parse(&["mprovision", "list", "--expires-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expires-in-days", "366"])).to(be_err());

        expect!(parse(&["mprovision", "list", "--filter", "abc", "--expires-in-days", "3", "."]))
            .to(be_ok().value(Command::List(ListParams {
                filter: Some("abc".to_string()),
                expires_in_days: Some(3),
                directory: Some(".".into()),
            })));
    }

    #[test]
    fn cleanup_command() {
        expect!(parse(&["mprovision", "cleanup"])).to(be_ok().value(Command::Cleanup(None)));

        expect!(parse(&["mprovision", "cleanup", "."]))
            .to(be_ok().value(Command::Cleanup(Some(".".into()))));
    }
}
<|endoftext|>


<commit_before>pub use self::parse_mode::ParseMode;
pub use self::chat_action::ChatAction;

mod parse_mode;
mod chat_action;

use reqwest::Client;
use serde_json;
use serde_json::Value;

use bot::chat_action::{get_chat_action};
use bot::parse_mode::{get_parse_mode};
use error::{Result, check_for_error};
use error::Error::{RequestFailed, JsonNotFound};
use objects::{Update, Message, Contact, InlineKeyboardMarkup};
use value_extension::ValueExtension;

/// A `Bot` which will do all the API calls.
///
/// The `Bot` will be given access to in a `Command` with which you can do all
/// the API interactions in your `Command`s.
#[derive(Debug)]
pub struct Bot {
    pub id: i64,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: String,
    client: Client,
    pub bot_url: String,
}

impl Bot {
    /// Constructs a new `Bot`.
    pub fn new(bot_url: String) -> Result<Self> {
        let client = Client::new()?;
        let rjson = Bot::get_me(&client, &bot_url)?;
        let id = rjson.as_required_i64("id")?;
        let first_name = rjson.as_required_string("first_name")?;
        let last_name = rjson.as_optional_string("last_name");
        let username = rjson.as_required_string("username")?;

        Ok(Bot {
            id: id,
            first_name: first_name,
            last_name: last_name,
            username: username,
            client: client,
            bot_url: bot_url,
        })
    }

    /// API call which gets the information about your bot.
    pub fn get_me(client: &Client, bot_url: &str) -> Result<Value> {
        let path = ["getMe"];
        let url = ::construct_api_url(bot_url, &path);
        let mut resp = client.get(&url).send()?;

        if resp.status().is_success() {
            let rjson: Value = resp.json()?;
            rjson.get("result").cloned().ok_or(JsonNotFound)
        } else {
            Err(RequestFailed(*resp.status()))
        }
    }

    /// API call which will get called to get the updates for your bot.
    pub fn get_updates(&self,
                       offset: i32,
                       limit: Option<i32>,
                       timeout: Option<i32>,
                       network_delay: Option<i32>)
                       -> Result<Option<Vec<Update>>> {
        let limit = limit.unwrap_or(100);
        let timeout = timeout.unwrap_or(0);
        // Use network_delay when it gets implemented
        let network_delay = network_delay.unwrap_or(5);
        let path = ["getUpdates"];
        let path_url = ::construct_api_url(&self.bot_url, &path);
        let url = format!("{}?offset={}&limit={}&timeout={}",
                          path_url,
                          offset,
                          limit,
                          timeout);
        let mut data = self.client.get(&url).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let updates_json = rjson.get("result");

        if let Some(result) = updates_json {
            let updates: Vec<Update> = serde_json::from_value(result.clone())?;
            Ok(Some(updates))
        } else {
            Ok(None)
        }
    }

    /// API call which will send a message to a chat which your bot participates in.
    pub fn send_message(&self,
                        chat_id: &i64,
                        text: &str,
                        parse_mode: Option<&ParseMode>,
                        disable_web_page_preview: Option<&bool>,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let parse_mode = &get_parse_mode(parse_mode.unwrap_or(&ParseMode::Text));
        let disable_web_page_preview: &str = &disable_web_page_preview.unwrap_or(&false)
            .to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id.map(|i| i.to_string())
            .unwrap_or("None".to_string());
        let reply_markup = &reply_markup.map(|r| serde_json::to_string(r).unwrap_or("".to_string())).unwrap_or("".to_string());
        let path = ["sendMessage"];
        let params = [("chat_id", chat_id),
                      ("text", text),
                      ("parse_mode", parse_mode),
                      ("disable_web_page_preview", disable_web_page_preview),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// API call which will reply to a message directed to your bot.
    pub fn reply_to_message(&self, update: &Update, text: &str) -> Result<Message> {
        let message = update.clone().message.unwrap();
        let message_id = message.message_id;
        let chat_id = message.chat.id;
        self.send_message(&chat_id, text, None, None, None, Some(&message_id), None)
    }

    /// API call which will forward a message.
    pub fn forward_messge(&self,
                          update: &Update,
                          chat_id: &i64,
                          disable_notification: Option<&bool>)
                          -> Result<Message> {
        let message = update.clone().message.unwrap();
        let chat_id: &str = &chat_id.to_string();
        let from_chat_id: &str = &message.chat.id.to_string();
        let message_id: &str = &message.message_id.to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let path = ["forwardMessage"];
        let params = [("chat_id", chat_id),
                      ("from_chat_id", from_chat_id),
                      ("disable_notification", disable_notification),
                      ("message_id", message_id)];
        self.post_message(&path, &params)
    }

    /// API call which will send the requested chat action to your bot.
    pub fn send_chat_action(&self, chat_id: &i64, action: &ChatAction) -> Result<bool> {
        let chat_id: &str = &chat_id.to_string();
        let action = &get_chat_action(action);
        let path = ["sendChatAction"];
        let params = [("chat_id", chat_id),
                      ("action", action)];

        let url = ::construct_api_url(&self.bot_url, &path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let result_json = rjson.get("result").ok_or(JsonNotFound)?;
        let result: bool = serde_json::from_value(result_json.clone())?;
        Ok(result)
    }

    /// API call which will send the given contact.
    pub fn send_contact(&self,
                        chat_id: &i64,
                        contact: &Contact,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>) -> Result<Message> {

        let chat_id: &str = &chat_id.to_string();
        let phone_number = &contact.phone_number;
        let first_name = &contact.first_name;
        let last_name = &contact.clone().last_name.unwrap();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id.map(|i| i.to_string())
            .unwrap_or("None".to_string());
        let reply_markup = &reply_markup.and_then(|r| serde_json::to_string(r).ok()).unwrap_or("".to_string());

        let path = ["sendContact"];
        let params = [("chat_id", chat_id),
                      ("phone_number", phone_number),
                      ("first_name", first_name),
                      ("last_name", last_name),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// The actual networking done for sending messages.
    fn post_message(&self, path: &[&str], params: &[(&str, &str)]) -> Result<Message> {
        let url = ::construct_api_url(&self.bot_url, path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let message_json = rjson.get("result").ok_or(JsonNotFound)?;
        let message: Message = serde_json::from_value(message_json.clone())?;
        Ok(message)
    }
}
<commit_msg>changed result to chat_action_succeeded<commit_after>pub use self::parse_mode::ParseMode;
pub use self::chat_action::ChatAction;

mod parse_mode;
mod chat_action;

use reqwest::Client;
use serde_json;
use serde_json::Value;

use bot::chat_action::{get_chat_action};
use bot::parse_mode::{get_parse_mode};
use error::{Result, check_for_error};
use error::Error::{RequestFailed, JsonNotFound};
use objects::{Update, Message, Contact, InlineKeyboardMarkup};
use value_extension::ValueExtension;

/// A `Bot` which will do all the API calls.
///
/// The `Bot` will be given access to in a `Command` with which you can do all
/// the API interactions in your `Command`s.
#[derive(Debug)]
pub struct Bot {
    pub id: i64,
    pub first_name: String,
    pub last_name: Option<String>,
    pub username: String,
    client: Client,
    pub bot_url: String,
}

impl Bot {
    /// Constructs a new `Bot`.
    pub fn new(bot_url: String) -> Result<Self> {
        let client = Client::new()?;
        let rjson = Bot::get_me(&client, &bot_url)?;
        let id = rjson.as_required_i64("id")?;
        let first_name = rjson.as_required_string("first_name")?;
        let last_name = rjson.as_optional_string("last_name");
        let username = rjson.as_required_string("username")?;

        Ok(Bot {
            id: id,
            first_name: first_name,
            last_name: last_name,
            username: username,
            client: client,
            bot_url: bot_url,
        })
    }

    /// API call which gets the information about your bot.
    pub fn get_me(client: &Client, bot_url: &str) -> Result<Value> {
        let path = ["getMe"];
        let url = ::construct_api_url(bot_url, &path);
        let mut resp = client.get(&url).send()?;

        if resp.status().is_success() {
            let rjson: Value = resp.json()?;
            rjson.get("result").cloned().ok_or(JsonNotFound)
        } else {
            Err(RequestFailed(*resp.status()))
        }
    }

    /// API call which will get called to get the updates for your bot.
    pub fn get_updates(&self,
                       offset: i32,
                       limit: Option<i32>,
                       timeout: Option<i32>,
                       network_delay: Option<i32>)
                       -> Result<Option<Vec<Update>>> {
        let limit = limit.unwrap_or(100);
        let timeout = timeout.unwrap_or(0);
        // Use network_delay when it gets implemented
        let network_delay = network_delay.unwrap_or(5);
        let path = ["getUpdates"];
        let path_url = ::construct_api_url(&self.bot_url, &path);
        let url = format!("{}?offset={}&limit={}&timeout={}",
                          path_url,
                          offset,
                          limit,
                          timeout);
        let mut data = self.client.get(&url).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let updates_json = rjson.get("result");

        if let Some(result) = updates_json {
            let updates: Vec<Update> = serde_json::from_value(result.clone())?;
            Ok(Some(updates))
        } else {
            Ok(None)
        }
    }

    /// API call which will send a message to a chat which your bot participates in.
    pub fn send_message(&self,
                        chat_id: &i64,
                        text: &str,
                        parse_mode: Option<&ParseMode>,
                        disable_web_page_preview: Option<&bool>,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>)
                        -> Result<Message> {
        let chat_id: &str = &chat_id.to_string();
        let parse_mode = &get_parse_mode(parse_mode.unwrap_or(&ParseMode::Text));
        let disable_web_page_preview: &str = &disable_web_page_preview.unwrap_or(&false)
            .to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id.map(|i| i.to_string())
            .unwrap_or("None".to_string());
        let reply_markup = &reply_markup.map(|r| serde_json::to_string(r).unwrap_or("".to_string())).unwrap_or("".to_string());
        let path = ["sendMessage"];
        let params = [("chat_id", chat_id),
                      ("text", text),
                      ("parse_mode", parse_mode),
                      ("disable_web_page_preview", disable_web_page_preview),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// API call which will reply to a message directed to your bot.
    pub fn reply_to_message(&self, update: &Update, text: &str) -> Result<Message> {
        let message = update.clone().message.unwrap();
        let message_id = message.message_id;
        let chat_id = message.chat.id;
        self.send_message(&chat_id, text, None, None, None, Some(&message_id), None)
    }

    /// API call which will forward a message.
    pub fn forward_messge(&self,
                          update: &Update,
                          chat_id: &i64,
                          disable_notification: Option<&bool>)
                          -> Result<Message> {
        let message = update.clone().message.unwrap();
        let chat_id: &str = &chat_id.to_string();
        let from_chat_id: &str = &message.chat.id.to_string();
        let message_id: &str = &message.message_id.to_string();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let path = ["forwardMessage"];
        let params = [("chat_id", chat_id),
                      ("from_chat_id", from_chat_id),
                      ("disable_notification", disable_notification),
                      ("message_id", message_id)];
        self.post_message(&path, &params)
    }

    /// API call which will send the requested chat action to your bot.
    pub fn send_chat_action(&self, chat_id: &i64, action: &ChatAction) -> Result<bool> {
        let chat_id: &str = &chat_id.to_string();
        let action = &get_chat_action(action);
        let path = ["sendChatAction"];
        let params = [("chat_id", chat_id),
                      ("action", action)];

        let url = ::construct_api_url(&self.bot_url, &path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let result_json = rjson.get("result").ok_or(JsonNotFound)?;
        let chat_action_succeeded: bool = serde_json::from_value(result_json.clone())?;
        Ok(chat_action_succeeded)
    }

    /// API call which will send the given contact.
    pub fn send_contact(&self,
                        chat_id: &i64,
                        contact: &Contact,
                        disable_notification: Option<&bool>,
                        reply_to_message_id: Option<&i64>,
                        reply_markup: Option<&InlineKeyboardMarkup>) -> Result<Message> {

        let chat_id: &str = &chat_id.to_string();
        let phone_number = &contact.phone_number;
        let first_name = &contact.first_name;
        let last_name = &contact.clone().last_name.unwrap();
        let disable_notification: &str = &disable_notification.unwrap_or(&false).to_string();
        let reply_to_message_id: &str = &reply_to_message_id.map(|i| i.to_string())
            .unwrap_or("None".to_string());
        let reply_markup = &reply_markup.and_then(|r| serde_json::to_string(r).ok()).unwrap_or("".to_string());

        let path = ["sendContact"];
        let params = [("chat_id", chat_id),
                      ("phone_number", phone_number),
                      ("first_name", first_name),
                      ("last_name", last_name),
                      ("disable_notification", disable_notification),
                      ("reply_to_message_id", reply_to_message_id),
                      ("reply_markup", reply_markup)];
        self.post_message(&path, &params)
    }

    /// The actual networking done for sending messages.
    fn post_message(&self, path: &[&str], params: &[(&str, &str)]) -> Result<Message> {
        let url = ::construct_api_url(&self.bot_url, path);
        let mut data = self.client.post(&url).form(&params).send()?;
        let rjson: Value = check_for_error(data.json()?)?;
        let message_json = rjson.get("result").ok_or(JsonNotFound)?;
        let message: Message = serde_json::from_value(message_json.clone())?;
        Ok(message)
    }
}
<|endoftext|>


<commit_before>use std::mem::size_of_val;
use std::owned::Box;

struct Point {
    x: f64,
    y: f64,
}

struct Rectangle {
    p1: Point,
    p2: Point,
}

fn origin() -> Point {
    Point { x: 0.0, y: 0.0 }
}

fn boxed_origin() -> Box<Point> {
    // heap allocate this point, and return a pointer to it
    box Point { x: 0.0, y: 0.0 }
}

fn main() {
    // all type annotations are superfluous
    // stack allocated variables
    let point: Point = origin();
    let rectangle: Rectangle = Rectangle {
        p1: origin(),
        p2: Point { x: 3.0, y: 4.0 }
    };

    // heap allocated rectangle
    let boxed_rectangle: Box<Rectangle> = box Rectangle {
        p1: origin(),
        p2: origin()
    };

    // function output can be boxed
    let boxed_point: Box<Point> = box origin();

    // double indirection
    let box_in_a_box: Box<Box<Point>> = box boxed_origin();

    println!("Point occupies {} bytes in the stack",
             size_of_val(&point));
    // The `&` means that we are passing the object by reference, rather
    // than by value. In Rust, this is known as *borrowing* and will be
    // covered in detail in its own section.
    println!("Rectangle occupies {} bytes in the stack",
             size_of_val(&rectangle));

    // box size = pointer size
    println!("Boxed point occupies {} bytes in the stack",
             size_of_val(&boxed_point));
    println!("Boxed rectangle occupies {} bytes in the stack",
             size_of_val(&boxed_rectangle));

    // copy data of boxed_point into the stack
    let unboxed_point: Point = *boxed_point;
    println!("Unboxed point occupies {} bytes in the stack",
             size_of_val(&unboxed_point));

    // unboxing via deconstruction
    let box another_unboxed_point = boxed_point;
    println!("Another unboxed point occupies {} bytes in the stack",
             size_of_val(&unboxed_point));
}
<commit_msg>box: fix vairable name<commit_after>use std::mem::size_of_val;
use std::owned::Box;

struct Point {
    x: f64,
    y: f64,
}

struct Rectangle {
    p1: Point,
    p2: Point,
}

fn origin() -> Point {
    Point { x: 0.0, y: 0.0 }
}

fn boxed_origin() -> Box<Point> {
    // heap allocate this point, and return a pointer to it
    box Point { x: 0.0, y: 0.0 }
}

fn main() {
    // all type annotations are superfluous
    // stack allocated variables
    let point: Point = origin();
    let rectangle: Rectangle = Rectangle {
        p1: origin(),
        p2: Point { x: 3.0, y: 4.0 }
    };

    // heap allocated rectangle
    let boxed_rectangle: Box<Rectangle> = box Rectangle {
        p1: origin(),
        p2: origin()
    };

    // function output can be boxed
    let boxed_point: Box<Point> = box origin();

    // double indirection
    let box_in_a_box: Box<Box<Point>> = box boxed_origin();

    println!("Point occupies {} bytes in the stack",
             size_of_val(&point));
    // The `&` means that we are passing the object by reference, rather
    // than by value. In Rust, this is known as *borrowing* and will be
    // covered in detail in its own section.
    println!("Rectangle occupies {} bytes in the stack",
             size_of_val(&rectangle));

    // box size = pointer size
    println!("Boxed point occupies {} bytes in the stack",
             size_of_val(&boxed_point));
    println!("Boxed rectangle occupies {} bytes in the stack",
             size_of_val(&boxed_rectangle));

    // copy data of boxed_point into the stack
    let unboxed_point: Point = *boxed_point;
    println!("Unboxed point occupies {} bytes in the stack",
             size_of_val(&unboxed_point));

    // unboxing via deconstruction
    let box another_unboxed_point = boxed_point;
    println!("Another unboxed point occupies {} bytes in the stack",
             size_of_val(&another_unboxed_point));
}
<|endoftext|>


<commit_before>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::libc::{c_int, c_void, c_char, size_t};
use std::os;
use fuse::{fuse_args, fuse_mount_compat25};
#[cfg(not(target_os = "macos"))]
use fuse::fuse_unmount_compat22;

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code)]
#[allow(non_camel_case_types)]
mod libc {
	use std::libc::{c_char, c_int, c_void, size_t, ssize_t};

	/// Iovec data structure for readv and writev calls.
	pub struct iovec {
		iov_base: *c_void,
		iov_len: size_t,
	}

	extern "system" {
		/// Read data from fd into multiple buffers
		pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
		/// Write data from multiple buffers to fd
		pub fn writev (fd: c_int, iov: *iovec, iovcnt: c_int) -> ssize_t;

		pub fn realpath (file_name: *c_char, resolved_name: *mut c_char) -> *c_char;

		#[cfg(target_os = "macos")]
		pub fn unmount(dir: *c_char, flags: c_int) -> c_int;
	}

	#[cfg(target_os = "macos")]
	pub static PATH_MAX: int = 1024;

	#[cfg(target_os = "linux")]
	pub static PATH_MAX: int = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
fn real_path (path: &PosixPath) -> Result<PosixPath, c_int> {
	path.with_c_str(|p| {
		let mut resolved = [0 as c_char, ..libc::PATH_MAX];
		unsafe {
			let rp = libc::realpath(p, resolved.as_mut_ptr());
			if rp.is_null() {
				Err(::std::os::errno() as c_int)
			} else {
				Ok(PosixPath::new(::std::c_str::CString::new(rp,false)))
			}
		}
	})
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T> (options: &[&[u8]], f: |&fuse_args| -> T) -> T {
	let progname = "rust-fuse";
	let args = Vec::from_fn(1+options.len(), |i| {
		match i {
			0 => progname.to_c_str(),
			_ => options[i-1].to_c_str(),
		}
	});
	let argptrs: Vec<*c_char> = args.iter().map(|s| s.with_ref(|s| s)).collect();
	f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
	priv mountpoint: Path,
	priv fd: c_int,
}

impl Channel {
	/// Create a new communication channel to the kernel driver by mounting the
	/// given path. The kernel driver will delegate filesystem operations of
	/// the given path to the channel. If the channel is dropped, the path is
	/// unmounted.
	pub fn new (mountpoint: &Path, options: &[&[u8]]) -> Result<Channel, c_int> {
		real_path(mountpoint).and_then(|mountpoint| mountpoint.with_c_str(|mnt| {
			with_fuse_args(options, |args| {
				let fd = unsafe { fuse_mount_compat25(mnt, args) };
				if fd < 0 {
					Err(os::errno() as c_int)
				} else {
					Ok(Channel { mountpoint: mountpoint.clone(), fd: fd })
				}
			})
		}))
	}

	/// Receives data up to the size of the given buffer and returns the size
	/// of the received data.
	/// Note: Can block natively, so it should be called from a separate thread
	pub fn receive (&self, buffer: &mut [u8]) -> Result<uint, c_int> {
		let rc = unsafe { ::std::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.len() as size_t) };
		if rc < 0 {
			Err(os::errno() as c_int)
		} else {
			Ok(rc as uint)
		}
	}

	/// Returns a sender object for this channel. The sender object can be
	/// used to send to the channel. Multiple sender objects can be used
	/// and they can safely be sent to other tasks.
	pub fn sender (&self) -> ChannelSender {
		// Since write/writev syscalls are threadsafe, we can simply create
		// a sender by using the same fd and use it in other threads. Only
		// the channel closes the fd when dropped. If any sender is used after
		// dropping the channel, it'll return an EBADF error.
		ChannelSender { fd: self.fd }
	}
}

impl Drop for Channel {
	fn drop (&mut self) {
		// TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
		// Close the communication channel to the kernel driver
		// (closing it before unnmount prevents sync unmount deadlock)
		unsafe { ::std::libc::close(self.fd); }
		// Unmount this channel's mount point
		unmount(&self.mountpoint);
	}
}

pub struct ChannelSender {
	priv fd: c_int,
}

impl ChannelSender {
	/// Send all data in the slice of slice of bytes in a single write.
	/// Note: Can block natively, so it should be called from a separate thread
	pub fn send (&self, buffer: &[&[u8]]) -> Result<(), c_int> {
		let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
			libc::iovec { iov_base: d.as_ptr() as *c_void, iov_len: d.len() as size_t }
		}).collect();
		let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
		if rc < 0 {
			Err(os::errno() as c_int)
		} else {
			Ok(())
		}
	}
}

// On OS X, fuse_unmount_compat22 attempts to call realpath, which in turn calls into the filesystem.
// If the filesystem returns an error, the unmount does not take place, with no indication of the error
// available to the caller.  So we call unmount directly, which is what osxfuse does anyway, since
// we already converted to the real path when we first mounted.
#[cfg(target_os = "macos")]
/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) {
	mountpoint.with_c_str(|mnt| {
		unsafe { libc::unmount(mnt, 0); }
	})
}

#[cfg(not(target_os = "macos"))]
/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) {
	mountpoint.with_c_str(|mnt| {
		unsafe { fuse_unmount_compat22(mnt); }
	});
}

#[cfg(test)]
mod test {
	use super::with_fuse_args;
	use std::slice;

	#[test]
	fn fuse_args () {
		with_fuse_args([bytes!("foo"), bytes!("bar")], |args| {
			unsafe {
				assert!(args.argc == 3);
				slice::raw::buf_as_slice(*args.argv.offset(0) as *u8, 10, |bytes| { assert!(bytes == bytes!("rust-fuse\0") ); });
				slice::raw::buf_as_slice(*args.argv.offset(1) as *u8,  4, |bytes| { assert!(bytes == bytes!("foo\0")); });
				slice::raw::buf_as_slice(*args.argv.offset(2) as *u8,  4, |bytes| { assert!(bytes == bytes!("bar\0")); });
			}
		});
	}
}
<commit_msg>Don't use dynamically sized vectors where slices are sufficient<commit_after>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::{os, slice};
use std::libc::{c_int, c_void, c_char, size_t};
use fuse::{fuse_args, fuse_mount_compat25};
#[cfg(not(target_os = "macos"))]
use fuse::fuse_unmount_compat22;

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code)]
#[allow(non_camel_case_types)]
mod libc {
	use std::libc::{c_char, c_int, c_void, size_t, ssize_t};

	/// Iovec data structure for readv and writev calls.
	pub struct iovec {
		iov_base: *c_void,
		iov_len: size_t,
	}

	extern "system" {
		/// Read data from fd into multiple buffers
		pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
		/// Write data from multiple buffers to fd
		pub fn writev (fd: c_int, iov: *iovec, iovcnt: c_int) -> ssize_t;

		pub fn realpath (file_name: *c_char, resolved_name: *mut c_char) -> *c_char;

		#[cfg(target_os = "macos")]
		pub fn unmount(dir: *c_char, flags: c_int) -> c_int;
	}

	#[cfg(target_os = "macos")]
	pub static PATH_MAX: int = 1024;

	#[cfg(target_os = "linux")]
	pub static PATH_MAX: int = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
fn real_path (path: &PosixPath) -> Result<PosixPath, c_int> {
	path.with_c_str(|p| {
		let mut resolved = [0 as c_char, ..libc::PATH_MAX];
		unsafe {
			let rp = libc::realpath(p, resolved.as_mut_ptr());
			if rp.is_null() {
				Err(::std::os::errno() as c_int)
			} else {
				Ok(PosixPath::new(::std::c_str::CString::new(rp,false)))
			}
		}
	})
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T> (options: &[&[u8]], f: |&fuse_args| -> T) -> T {
	let progname = "rust-fuse";
	let args = slice::from_fn(1+options.len(), |i| {
		match i {
			0 => progname.to_c_str(),
			_ => options[i-1].to_c_str(),
		}
	});
	let argptrs = args.map(|s| s.with_ref(|s| s));
	f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
	priv mountpoint: Path,
	priv fd: c_int,
}

impl Channel {
	/// Create a new communication channel to the kernel driver by mounting the
	/// given path. The kernel driver will delegate filesystem operations of
	/// the given path to the channel. If the channel is dropped, the path is
	/// unmounted.
	pub fn new (mountpoint: &Path, options: &[&[u8]]) -> Result<Channel, c_int> {
		real_path(mountpoint).and_then(|mountpoint| mountpoint.with_c_str(|mnt| {
			with_fuse_args(options, |args| {
				let fd = unsafe { fuse_mount_compat25(mnt, args) };
				if fd < 0 {
					Err(os::errno() as c_int)
				} else {
					Ok(Channel { mountpoint: mountpoint.clone(), fd: fd })
				}
			})
		}))
	}

	/// Receives data up to the size of the given buffer and returns the size
	/// of the received data.
	/// Note: Can block natively, so it should be called from a separate thread
	pub fn receive (&self, buffer: &mut [u8]) -> Result<uint, c_int> {
		let rc = unsafe { ::std::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.len() as size_t) };
		if rc < 0 {
			Err(os::errno() as c_int)
		} else {
			Ok(rc as uint)
		}
	}

	/// Returns a sender object for this channel. The sender object can be
	/// used to send to the channel. Multiple sender objects can be used
	/// and they can safely be sent to other tasks.
	pub fn sender (&self) -> ChannelSender {
		// Since write/writev syscalls are threadsafe, we can simply create
		// a sender by using the same fd and use it in other threads. Only
		// the channel closes the fd when dropped. If any sender is used after
		// dropping the channel, it'll return an EBADF error.
		ChannelSender { fd: self.fd }
	}
}

impl Drop for Channel {
	fn drop (&mut self) {
		// TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
		// Close the communication channel to the kernel driver
		// (closing it before unnmount prevents sync unmount deadlock)
		unsafe { ::std::libc::close(self.fd); }
		// Unmount this channel's mount point
		unmount(&self.mountpoint);
	}
}

pub struct ChannelSender {
	priv fd: c_int,
}

impl ChannelSender {
	/// Send all data in the slice of slice of bytes in a single write.
	/// Note: Can block natively, so it should be called from a separate thread
	pub fn send (&self, buffer: &[&[u8]]) -> Result<(), c_int> {
		let iovecs = buffer.map(|d| {
			libc::iovec { iov_base: d.as_ptr() as *c_void, iov_len: d.len() as size_t }
		});
		let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
		if rc < 0 {
			Err(os::errno() as c_int)
		} else {
			Ok(())
		}
	}
}

// On OS X, fuse_unmount_compat22 attempts to call realpath, which in turn calls into the filesystem.
// If the filesystem returns an error, the unmount does not take place, with no indication of the error
// available to the caller.  So we call unmount directly, which is what osxfuse does anyway, since
// we already converted to the real path when we first mounted.
#[cfg(target_os = "macos")]
/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) {
	mountpoint.with_c_str(|mnt| {
		unsafe { libc::unmount(mnt, 0); }
	})
}

#[cfg(not(target_os = "macos"))]
/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) {
	mountpoint.with_c_str(|mnt| {
		unsafe { fuse_unmount_compat22(mnt); }
	});
}

#[cfg(test)]
mod test {
	use super::with_fuse_args;
	use std::slice;

	#[test]
	fn fuse_args () {
		with_fuse_args([bytes!("foo"), bytes!("bar")], |args| {
			unsafe {
				assert!(args.argc == 3);
				slice::raw::buf_as_slice(*args.argv.offset(0) as *u8, 10, |bytes| { assert!(bytes == bytes!("rust-fuse\0") ); });
				slice::raw::buf_as_slice(*args.argv.offset(1) as *u8,  4, |bytes| { assert!(bytes == bytes!("foo\0")); });
				slice::raw::buf_as_slice(*args.argv.offset(2) as *u8,  4, |bytes| { assert!(bytes == bytes!("bar\0")); });
			}
		});
	}
}
<|endoftext|>


<commit_before>use std::iter::Peekable;

/// An iterator that performs a lookahead of 1, utilizing the existing Peakable Iterator
/// Can be stacked to perform an even greater lookahead
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct Peeking<I, A> where
    A: Clone,
    I: Iterator<Item=A>,
{
    iter: Peekable<I>,
}

impl<I, A> Iterator for Peeking<I, A> where
    A: Clone,
    I: Iterator<Item=A>
{
    type Item = (A, Option<A>);

    #[inline]
    fn next(&mut self) -> Option<(A, Option<A>)> {
        match self.iter.next() {
            Some(item) => Some((item,
                match self.iter.peek() {
                    Some(ref item) => Some((*item).clone()),
                    None => None,
                }
            )),
            None => None,
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

pub trait PeekingExt {
    fn peeking(self) -> Peeking<Self, Self::Item>
        where Self: Sized+Iterator,
              Self::Item: Clone;
}

impl<A: Clone, I: Sized+Iterator<Item=A>> PeekingExt for I {
    fn peeking(self) -> Peeking<Self, A>
        where Self: Sized+Iterator
    {
         Peeking { iter: self.peekable() }
    }
}

/// An iterator to maintain state while iterating another iterator and allows filtering
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct FilteringScan<I, St, F> {
    iter: I,
    f: F,
    state: St,
}

impl<B, I, St, F> Iterator for FilteringScan<I, St, F> where
    I: Iterator,
    F: FnMut(&mut St, I::Item) -> Option<B>,
{
    type Item = B;

    #[inline]
    fn next(&mut self) -> Option<B> {
        loop {
            match self.iter.next() {
                Some(next) => match (self.f)(&mut self.state, next) {
                    Some(result) => return Some(result),
                    None => continue,
                },
                None => return None,
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, upper) = self.iter.size_hint();
        (0, upper) // can't know a lower bound, due to the scan function
    }
}

pub trait FilteringScanExt {
    fn scan_filter<St, B, F>(self, initial_state: St, f: F) -> FilteringScan<Self, St, F>
        where Self: Sized+Iterator, F: FnMut(&mut St, Self::Item) -> Option<B>;
}

impl<I: Sized+Iterator> FilteringScanExt for I {
    fn scan_filter<St, B, F>(self, initial_state: St, f: F) -> FilteringScan<Self, St, F>
        where Self: Sized+Iterator, F: FnMut(&mut St, I::Item) -> Option<B>,
    {
        FilteringScan{iter: self, f: f, state: initial_state}
    }
}

#[derive(Clone)]
pub enum ParseResult {
    Continue,
    Halt,
    End,
}

/// An iterator to maintain state while iterating another iterator and allows more complex parsing
// (allowing holding the current iterator value, continue if its goes empty and filter the output)
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct Parser<I, A, St, F> where
    A: Clone,
    I: Iterator<Item=A>,
{
    iter: I,
    f: F,
    state: St,

    iter_state: ParseResult,
    current_elem: Option<A>,
}

impl<B, I, St, F, A> Iterator for Parser<I, A, St, F> where
    A: Clone,
    I: Iterator<Item=A>,
    F: FnMut(&mut St, Option<I::Item>) -> (ParseResult, Option<B>),
{
    type Item = B;

    #[inline]
    fn next(&mut self) -> Option<B> {
        loop {
            match self.iter_state {
                ParseResult::Continue => self.current_elem = self.iter.next(),
                ParseResult::Halt => {},
                ParseResult::End => return None,
            };

            let (new_state, result) = (self.f) (&mut self.state, self.current_elem.clone());

            self.iter_state = new_state;

            match result {
                Some(result) => return Some(result),
                None => continue,
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, None) // can't know a lower or upper bound, due to the parse function
    }
}

pub trait ParserExt {
    fn parsing<St, B, F>(self, initial_state: St, f: F) -> Parser<Self, Self::Item, St, F>
        where Self: Sized+Iterator, Self::Item: Clone, F: Fn(&mut St, Option<Self::Item>) -> (ParseResult, Option<B>);
}

impl<A:Clone, I: Sized+Iterator<Item=A>>ParserExt for I {
    fn parsing<St, B, F>(self, initial_state: St, f: F) -> Parser<Self, A, St, F>
        where F: FnMut(&mut St, Option<A>) -> (ParseResult, Option<B>),
    {
        Parser{iter: self, f: f, state: initial_state, iter_state: ParseResult::Continue, current_elem: None}
    }
}

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct Constructor<I, St, B, F>
    where
    I: Iterator,
    B: Clone,
    F: FnMut(&mut St, &mut Option<B>, I::Item) -> Option<B> {
        iter: I,
        state: St,
        f: F,
        current_elem: Option<B>,
}

impl<I, St, B, F> Iterator for Constructor<I, St, B, F> where
    I: Iterator,
    B: Clone,
    F: FnMut(&mut St, &mut Option<B>, I::Item) -> Option<B>,
{
    type Item = B;

    #[inline]
    fn next(&mut self) -> Option<B> {
        while let Some(elem) =  self.iter.next() {
            match (self.f)(&mut self.state, &mut self.current_elem, elem) {
                Some(elem) => {
                    let return_val = self.current_elem.clone();
                    self.current_elem = Some(elem);
                    match return_val {
                        Some(value) => return Some(value),
                        None => continue,
                    }
                },
                None => continue,
            }
        }

        let result = self.current_elem.clone();
        self.current_elem = None;
        result
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, upper) = self.iter.size_hint();
        (0, upper) // can't know a lower bound, due to the waiting function
    }
}

pub trait ConstructorExt {
    fn construct_state<St, B, F>(self, state: St, f: F) -> Constructor<Self, St, B, F>
        where Self: Sized+Iterator, B: Clone, F: Fn(&mut St, &mut Option<B>, Self::Item) -> Option<B>;
}

impl<I: Sized+Iterator>ConstructorExt for I {
    fn construct_state<St, B, F>(self, state: St, f: F) -> Constructor<Self, St, B, F>
        where Self: Sized+Iterator, B: Clone, F: Fn(&mut St, &mut Option<B>, I::Item) -> Option<B>,
    {
        Constructor{iter: self, state: state, f: f, current_elem: None}
    }
}

#[test]
fn peeking_test() {
    let test = vec!["first", "second", "third"];
    let mut index = 0;
    for (elem, peek) in test.into_iter().peeking() {
        match elem {
            "first" => {
                assert_eq!(peek, Some("second"));
                index += 1;
            },
            "second" => {
                assert_eq!(peek, Some("third"));
                index += 1;
            },
            "third" => {
                assert_eq!(peek, None);
                index += 1;
            },
            _ => assert!(false),
        }
    }
    assert_eq!(index, 3);
}

#[test]
fn filtering_scan_test() {
    let test = vec![1, 3, 1, 3];
    let index = 0;
    let mut sum = 0;
    for elem in test.into_iter().scan_filter(index, |index, x| {
        *index += 1;
        if (*index % 2) == 1 {
            Some(x)
        } else {
            None
        }
    }) {
        sum += elem;
    }
    assert_eq!(sum, 2);
}

#[test]
fn parsing_test() {

    struct ParseState {
        one_halt_test: u32,
        two_counter: u32,
        one_more_test: bool,
    }

    let mut result = vec![];

    let test = vec![1, 2, 2, 3];
    for elem in test.into_iter().parsing(
        ParseState {
            one_halt_test: 3,
            two_counter: 1,
            one_more_test: true,
        },
        |state, elem| {
            match elem {
                Some(1) => {
                    state.one_halt_test -= 1;
                    if state.one_halt_test == 0 {
                        (ParseResult::Continue, Some(1))
                    } else {
                        (ParseResult::Halt, None)
                    }
                },
                Some(2) => {
                    state.two_counter += 1;
                    (ParseResult::Continue, Some(state.two_counter))
                },
                Some(3) => {
                    (ParseResult::Continue, Some(4))
                },
                Some(_) => { panic!("found a bug in rust") },
                None => {
                    if state.one_more_test {
                        state.one_more_test = false;
                        (ParseResult::Continue, Some(5))
                    } else {
                        (ParseResult::End, None)
                    }
                }
            }
        }
    ) {
        result.push(elem);
    }

    assert_eq!(result, vec![1, 2, 3, 4, 5]);
}

#[test]
fn construct_test() {
    use std::cell::Cell;

    let test: Vec<u8> = vec![3, 3, 3, 2, 4, 5, 2];

    let result : Vec<u8> = test.into_iter().construct_state(0, |ref mut _state, ref mut value, i| {
        if value.is_none() {
            return Some(Cell::new(i)); //first value
        }

        let old_val = value.as_ref().unwrap().get();
        value.as_ref().unwrap().set(old_val + i);

        if value.as_ref().unwrap().get() >= 5 {
            Some(Cell::new(0)) //new value
        } else {
            None //continue constucting
        }
    }).map(|x| { x.get() % 5 }).collect();

    assert_eq!(result, vec![1, 0, 4, 2]);
}
<commit_msg>Add multithreaded iterator<commit_after>use std::iter::Peekable;
use std::sync::mpsc;
use std::thread;

/// An iterator that performs a lookahead of 1, utilizing the existing Peakable Iterator
/// Can be stacked to perform an even greater lookahead
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct Peeking<I, A> where
    A: Clone,
    I: Iterator<Item=A>,
{
    iter: Peekable<I>,
}

impl<I, A> Iterator for Peeking<I, A> where
    A: Clone,
    I: Iterator<Item=A>
{
    type Item = (A, Option<A>);

    #[inline]
    fn next(&mut self) -> Option<(A, Option<A>)> {
        match self.iter.next() {
            Some(item) => Some((item,
                match self.iter.peek() {
                    Some(ref item) => Some((*item).clone()),
                    None => None,
                }
            )),
            None => None,
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

pub trait PeekingExt {
    fn peeking(self) -> Peeking<Self, Self::Item>
        where Self: Sized+Iterator,
              Self::Item: Clone;
}

impl<A: Clone, I: Sized+Iterator<Item=A>> PeekingExt for I {
    fn peeking(self) -> Peeking<Self, A>
        where Self: Sized+Iterator
    {
         Peeking { iter: self.peekable() }
    }
}

/// An iterator to maintain state while iterating another iterator and allows filtering
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct FilteringScan<I, St, F> {
    iter: I,
    f: F,
    state: St,
}

impl<B, I, St, F> Iterator for FilteringScan<I, St, F> where
    I: Iterator,
    F: FnMut(&mut St, I::Item) -> Option<B>,
{
    type Item = B;

    #[inline]
    fn next(&mut self) -> Option<B> {
        loop {
            match self.iter.next() {
                Some(next) => match (self.f)(&mut self.state, next) {
                    Some(result) => return Some(result),
                    None => continue,
                },
                None => return None,
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, upper) = self.iter.size_hint();
        (0, upper) // can't know a lower bound, due to the scan function
    }
}

pub trait FilteringScanExt {
    fn scan_filter<St, B, F>(self, initial_state: St, f: F) -> FilteringScan<Self, St, F>
        where Self: Sized+Iterator, F: FnMut(&mut St, Self::Item) -> Option<B>;
}

impl<I: Sized+Iterator> FilteringScanExt for I {
    fn scan_filter<St, B, F>(self, initial_state: St, f: F) -> FilteringScan<Self, St, F>
        where Self: Sized+Iterator, F: FnMut(&mut St, I::Item) -> Option<B>,
    {
        FilteringScan{iter: self, f: f, state: initial_state}
    }
}

#[derive(Clone)]
pub enum ParseResult {
    Continue,
    Halt,
    End,
}

/// An iterator to maintain state while iterating another iterator and allows more complex parsing
// (allowing holding the current iterator value, continue if its goes empty and filter the output)
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct Parser<I, A, St, F> where
    A: Clone,
    I: Iterator<Item=A>,
{
    iter: I,
    f: F,
    state: St,

    iter_state: ParseResult,
    current_elem: Option<A>,
}

impl<B, I, St, F, A> Iterator for Parser<I, A, St, F> where
    A: Clone,
    I: Iterator<Item=A>,
    F: FnMut(&mut St, Option<I::Item>) -> (ParseResult, Option<B>),
{
    type Item = B;

    #[inline]
    fn next(&mut self) -> Option<B> {
        loop {
            match self.iter_state {
                ParseResult::Continue => self.current_elem = self.iter.next(),
                ParseResult::Halt => {},
                ParseResult::End => return None,
            };

            let (new_state, result) = (self.f) (&mut self.state, self.current_elem.clone());

            self.iter_state = new_state;

            match result {
                Some(result) => return Some(result),
                None => continue,
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, None) // can't know a lower or upper bound, due to the parse function
    }
}

pub trait ParserExt {
    fn parsing<St, B, F>(self, initial_state: St, f: F) -> Parser<Self, Self::Item, St, F>
        where Self: Sized+Iterator, Self::Item: Clone, F: Fn(&mut St, Option<Self::Item>) -> (ParseResult, Option<B>);
}

impl<A:Clone, I: Sized+Iterator<Item=A>>ParserExt for I {
    fn parsing<St, B, F>(self, initial_state: St, f: F) -> Parser<Self, A, St, F>
        where F: FnMut(&mut St, Option<A>) -> (ParseResult, Option<B>),
    {
        Parser{iter: self, f: f, state: initial_state, iter_state: ParseResult::Continue, current_elem: None}
    }
}

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct Constructor<I, St, B, F>
    where
    I: Iterator,
    B: Clone,
    F: FnMut(&mut St, &mut Option<B>, I::Item) -> Option<B> {
        iter: I,
        state: St,
        f: F,
        current_elem: Option<B>,
}

impl<I, St, B, F> Iterator for Constructor<I, St, B, F> where
    I: Iterator,
    B: Clone,
    F: FnMut(&mut St, &mut Option<B>, I::Item) -> Option<B>,
{
    type Item = B;

    #[inline]
    fn next(&mut self) -> Option<B> {
        while let Some(elem) =  self.iter.next() {
            match (self.f)(&mut self.state, &mut self.current_elem, elem) {
                Some(elem) => {
                    let return_val = self.current_elem.clone();
                    self.current_elem = Some(elem);
                    match return_val {
                        Some(value) => return Some(value),
                        None => continue,
                    }
                },
                None => continue,
            }
        }

        let result = self.current_elem.clone();
        self.current_elem = None;
        result
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let (_, upper) = self.iter.size_hint();
        (0, upper) // can't know a lower bound, due to the waiting function
    }
}

pub trait ConstructorExt {
    fn construct_state<St, B, F>(self, state: St, f: F) -> Constructor<Self, St, B, F>
        where Self: Sized+Iterator, B: Clone, F: Fn(&mut St, &mut Option<B>, Self::Item) -> Option<B>;
}

impl<I: Sized+Iterator>ConstructorExt for I {
    fn construct_state<St, B, F>(self, state: St, f: F) -> Constructor<Self, St, B, F>
        where Self: Sized+Iterator, B: Clone, F: Fn(&mut St, &mut Option<B>, I::Item) -> Option<B>,
    {
        Constructor{iter: self, state: state, f: f, current_elem: None}
    }
}

// An iterator that performs all iteration in a seperate therad and caches them
pub struct Cached<A: Send> {
    rx: mpsc::Receiver<A>
}

impl<A: Send> Iterator for Cached<A> {
    type Item = A;

    fn next(&mut self) -> Option<A> { self.rx.recv().ok() }
}

pub fn cached<A: Send + 'static, I: Sized + Iterator<Item=A>, F: FnOnce() -> I + Send + 'static>(constructor: F) -> (Cached<A>, thread::JoinHandle<()>)
{
    let (tx, rx) = mpsc::channel();
    let handle = thread::spawn(move || {
        for item in constructor() {
            let _ = tx.send(item);
        }
    });
    (Cached { rx: rx }, handle)
}

#[test]
fn peeking_test() {
    let test = vec!["first", "second", "third"];
    let mut index = 0;
    for (elem, peek) in test.into_iter().peeking() {
        match elem {
            "first" => {
                assert_eq!(peek, Some("second"));
                index += 1;
            },
            "second" => {
                assert_eq!(peek, Some("third"));
                index += 1;
            },
            "third" => {
                assert_eq!(peek, None);
                index += 1;
            },
            _ => assert!(false),
        }
    }
    assert_eq!(index, 3);
}

#[test]
fn filtering_scan_test() {
    let test = vec![1, 3, 1, 3];
    let index = 0;
    let mut sum = 0;
    for elem in test.into_iter().scan_filter(index, |index, x| {
        *index += 1;
        if (*index % 2) == 1 {
            Some(x)
        } else {
            None
        }
    }) {
        sum += elem;
    }
    assert_eq!(sum, 2);
}

#[test]
fn parsing_test() {

    struct ParseState {
        one_halt_test: u32,
        two_counter: u32,
        one_more_test: bool,
    }

    let mut result = vec![];

    let test = vec![1, 2, 2, 3];
    for elem in test.into_iter().parsing(
        ParseState {
            one_halt_test: 3,
            two_counter: 1,
            one_more_test: true,
        },
        |state, elem| {
            match elem {
                Some(1) => {
                    state.one_halt_test -= 1;
                    if state.one_halt_test == 0 {
                        (ParseResult::Continue, Some(1))
                    } else {
                        (ParseResult::Halt, None)
                    }
                },
                Some(2) => {
                    state.two_counter += 1;
                    (ParseResult::Continue, Some(state.two_counter))
                },
                Some(3) => {
                    (ParseResult::Continue, Some(4))
                },
                Some(_) => { panic!("found a bug in rust") },
                None => {
                    if state.one_more_test {
                        state.one_more_test = false;
                        (ParseResult::Continue, Some(5))
                    } else {
                        (ParseResult::End, None)
                    }
                }
            }
        }
    ) {
        result.push(elem);
    }

    assert_eq!(result, vec![1, 2, 3, 4, 5]);
}

#[test]
fn construct_test() {
    use std::cell::Cell;

    let test: Vec<u8> = vec![3, 3, 3, 2, 4, 5, 2];

    let result : Vec<u8> = test.into_iter().construct_state(0, |ref mut _state, ref mut value, i| {
        if value.is_none() {
            return Some(Cell::new(i)); //first value
        }

        let old_val = value.as_ref().unwrap().get();
        value.as_ref().unwrap().set(old_val + i);

        if value.as_ref().unwrap().get() >= 5 {
            Some(Cell::new(0)) //new value
        } else {
            None //continue constucting
        }
    }).map(|x| { x.get() % 5 }).collect();

    assert_eq!(result, vec![1, 0, 4, 2]);
}
<|endoftext|>