<commit_before>use std::vec::Vec;
use std::string::String;
use std::collections::HashMap;

struct Compiler {
    pub lines: Vec<String>,
    pub instructions: Vec<String>,
    pub labels: HashMap<String, u32>,
}

impl Compiler {
    pub fn new() -> Self {
        Compiler {
            lines: Vec::new(),
            instructions: Vec::new(),
            labels: HashMap::new(),
        }
    }

    pub fn parse(&mut self, assembly: &str) {
        self.split_lines(assembly);
        self.strip_comments();
        self.compile_labels();
    }

    fn compile_labels(&mut self) {
        let mut instructions = self.instructions.iter();
        let mut address = 0;

        while let Some(instruction) = instructions.next() {
            match instruction.find(':') {
                Some(index) => {
                    self.labels.insert(instruction.split_at(index).0.to_string(), address);
                }
                None => {
                    address += 4;
                }
            }
        }
    }

    fn strip_comments(&mut self) {
        let mut lines = self.lines.iter();
        while let Some(line) = lines.next() {
            let trimmed_line = line.trim();
            if !trimmed_line.is_empty() && !trimmed_line.starts_with(";") {
                let instruction = match trimmed_line.find(';') {
                    Some(index) => trimmed_line.split_at(index).0,
                    None => trimmed_line,
                };
                self.instructions.push(instruction.trim().to_string());
            }
        }
    }

    fn split_lines(&mut self, assembly: &str) {
        let mut line = String::new();
        let mut chars = assembly.chars();

        while let Some(c) = chars.next() {
            match c {
                // Line Feed | Vertical Tab | Form Feed | Next Line | Line/Paragraph Separator
                '\u{000A}' | '\u{000B}' | '\u{000C}' | '\u{0085}' | '\u{2028}' | '\u{2029}' => {
                    self.lines.push(line);
                    line = String::new();
                }

                // Carriage Return
                '\u{000D}' => {
                    self.lines.push(line);
                    line = String::new();

                    // Carriage Return + Line Feed
                    match chars.next() {
                        Some(n) => {
                            if n != '\u{000A}' {
                                line.push(n);
                            }
                        }
                        None => {}
                    }
                }

                _ => {
                    line.push(c);
                }
            }
        }

        if !line.is_empty() {
            self.lines.push(line);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Compiler;
    use std::collections::HashMap;

    #[test]
    fn it_splits_lines_by_line_feed() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\n0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_vertical_tab() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\x0B0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_form_feed() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\x0C0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_carriage_return() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\r0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_carriage_return_line_feed() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\r\n0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_next_line() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\u{0085}0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_line_separator() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\u{2028}0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_paragraph_separator() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\u{2029}0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_ignors_trailing_separators() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\r\n0 0 0 0\r\n");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_strips_single_line_comments() {
        let mut compiler = Compiler::new();
        compiler.parse("; single line comment\n0 0 0 0\n");

        assert_eq!(compiler.lines.len(), 2);
        assert_eq!(compiler.instructions.len(), 1);
    }

    #[test]
    fn it_strips_single_line_comments_starting_with_spaces() {
        let mut compiler = Compiler::new();
        compiler.parse(" ; single line comment\n0 0 0 0\n");

        assert_eq!(compiler.lines.len(), 2);
        assert_eq!(compiler.instructions.len(), 1);
    }

    #[test]
    fn it_strips_single_line_comments_starting_with_tabs() {
        let mut compiler = Compiler::new();
        compiler.parse("\t; single line comment\n0 0 0 0\n");

        assert_eq!(compiler.lines.len(), 2);
        assert_eq!(compiler.instructions.len(), 1);
    }

    #[test]
    fn it_strips_inline_comments() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0; inline comment\n");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_strips_inline_comments_starting_with_spaces() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0 ; inline comment\n");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_strips_inline_comments_starting_with_tabs() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\t; inline comment\n");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_trims_spaces_from_instructions() {
        let mut compiler = Compiler::new();
        compiler.parse(" 0 0 0 0 ");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_trims_tabs_from_instructions() {
        let mut compiler = Compiler::new();
        compiler.parse("\t0 0 0 0\t");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_compiles_addresses_for_labels() {
        let mut compiler = Compiler::new();
        compiler.parse("first:\nsecond:\n0 0 0 0\nthird:");

        let mut labels = HashMap::new();
        labels.insert("first".to_string(), 0);
        labels.insert("second".to_string(), 0);
        labels.insert("third".to_string(), 4);

        assert_eq!(compiler.labels, labels);
    }

    #[test]
    fn it_lets_the_last_label_win() {
        let mut compiler = Compiler::new();
        compiler.parse("label:\n0 0 0 0\nlabel:");

        let mut labels = HashMap::new();
        labels.insert("label".to_string(), 4);

        assert_eq!(compiler.labels, labels);
    }

    #[test]
    fn it_ignores_trailing_label_characters() {
        let mut compiler = Compiler::new();
        compiler.parse("label:with bits\n0 0 0 0\nlabel:with bytes");

        let mut labels = HashMap::new();
        labels.insert("label".to_string(), 4);

        assert_eq!(compiler.labels, labels);
    }
}
<commit_msg>Compile brk as opcode 0.<commit_after>use std::vec::Vec;
use std::string::String;
use std::collections::HashMap;

struct Compiler {
    pub lines: Vec<String>,
    pub instructions: Vec<String>,
    pub labels: HashMap<String, u32>,
    pub bytecodes: Vec<u32>,
}

impl Compiler {
    pub fn new() -> Self {
        Compiler {
            lines: Vec::new(),
            instructions: Vec::new(),
            labels: HashMap::new(),
            bytecodes: Vec::new(),
        }
    }

    pub fn parse(&mut self, assembly: &str) {
        self.split_lines(assembly);
        self.strip_comments();
        self.compile_labels();
        self.compile_bytecodes();
    }

    fn compile_bytecodes(&mut self) {
        let mut instructions = self.instructions.iter();

        while let Some(instruction) = instructions.next() {
            match instruction.find(':') {
                Some(_) => {
                    // Ignore labels
                }
                None => {
                    let mut bytecodes = instruction.split_whitespace();
                    match bytecodes.next() {
                        Some("brk") => {
                            self.bytecodes.push(0);
                        }
                        Some(opcode) => {
                            match u32::from_str_radix(opcode, 16) {
                                Ok(bytecode) => {
                                    self.bytecodes.push(bytecode);
                                }
                                _ => {}
                            }
                        }
                        None => {}
                    }
                }
            }
        }
    }

    fn compile_labels(&mut self) {
        let mut instructions = self.instructions.iter();
        let mut address = 0;

        while let Some(instruction) = instructions.next() {
            match instruction.find(':') {
                Some(index) => {
                    self.labels.insert(instruction.split_at(index).0.to_string(), address);
                }
                None => {
                    address += 4;
                }
            }
        }
    }

    fn strip_comments(&mut self) {
        let mut lines = self.lines.iter();
        while let Some(line) = lines.next() {
            let trimmed_line = line.trim();
            if !trimmed_line.is_empty() && !trimmed_line.starts_with(";") {
                let instruction = match trimmed_line.find(';') {
                    Some(index) => trimmed_line.split_at(index).0,
                    None => trimmed_line,
                };
                self.instructions.push(instruction.trim().to_string());
            }
        }
    }

    fn split_lines(&mut self, assembly: &str) {
        let mut line = String::new();
        let mut chars = assembly.chars();

        while let Some(c) = chars.next() {
            match c {
                // Line Feed | Vertical Tab | Form Feed | Next Line | Line/Paragraph Separator
                '\u{000A}' | '\u{000B}' | '\u{000C}' | '\u{0085}' | '\u{2028}' | '\u{2029}' => {
                    self.lines.push(line);
                    line = String::new();
                }

                // Carriage Return
                '\u{000D}' => {
                    self.lines.push(line);
                    line = String::new();

                    // Carriage Return + Line Feed
                    match chars.next() {
                        Some(n) => {
                            if n != '\u{000A}' {
                                line.push(n);
                            }
                        }
                        None => {}
                    }
                }

                _ => {
                    line.push(c);
                }
            }
        }

        if !line.is_empty() {
            self.lines.push(line);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Compiler;
    use std::collections::HashMap;

    #[test]
    fn it_splits_lines_by_line_feed() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\n0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_vertical_tab() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\x0B0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_form_feed() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\x0C0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_carriage_return() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\r0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_carriage_return_line_feed() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\r\n0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_next_line() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\u{0085}0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_line_separator() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\u{2028}0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_splits_lines_by_paragraph_separator() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\u{2029}0 0 0 0");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_ignors_trailing_separators() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\r\n0 0 0 0\r\n");

        assert_eq!(compiler.lines.len(), 2);
    }

    #[test]
    fn it_strips_single_line_comments() {
        let mut compiler = Compiler::new();
        compiler.parse("; single line comment\n0 0 0 0\n");

        assert_eq!(compiler.lines.len(), 2);
        assert_eq!(compiler.instructions.len(), 1);
    }

    #[test]
    fn it_strips_single_line_comments_starting_with_spaces() {
        let mut compiler = Compiler::new();
        compiler.parse(" ; single line comment\n0 0 0 0\n");

        assert_eq!(compiler.lines.len(), 2);
        assert_eq!(compiler.instructions.len(), 1);
    }

    #[test]
    fn it_strips_single_line_comments_starting_with_tabs() {
        let mut compiler = Compiler::new();
        compiler.parse("\t; single line comment\n0 0 0 0\n");

        assert_eq!(compiler.lines.len(), 2);
        assert_eq!(compiler.instructions.len(), 1);
    }

    #[test]
    fn it_strips_inline_comments() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0; inline comment\n");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_strips_inline_comments_starting_with_spaces() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0 ; inline comment\n");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_strips_inline_comments_starting_with_tabs() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0\t; inline comment\n");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_trims_spaces_from_instructions() {
        let mut compiler = Compiler::new();
        compiler.parse(" 0 0 0 0 ");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_trims_tabs_from_instructions() {
        let mut compiler = Compiler::new();
        compiler.parse("\t0 0 0 0\t");

        assert_eq!(compiler.lines.len(), 1);
        assert_eq!(compiler.instructions, vec!["0 0 0 0"]);
    }

    #[test]
    fn it_compiles_addresses_for_labels() {
        let mut compiler = Compiler::new();
        compiler.parse("first:\nsecond:\n0 0 0 0\nthird:");

        let mut labels = HashMap::new();
        labels.insert("first".to_string(), 0);
        labels.insert("second".to_string(), 0);
        labels.insert("third".to_string(), 4);

        assert_eq!(compiler.labels, labels);
    }

    #[test]
    fn it_lets_the_last_label_win() {
        let mut compiler = Compiler::new();
        compiler.parse("label:\n0 0 0 0\nlabel:");

        let mut labels = HashMap::new();
        labels.insert("label".to_string(), 4);

        assert_eq!(compiler.labels, labels);
    }

    #[test]
    fn it_ignores_trailing_label_characters() {
        let mut compiler = Compiler::new();
        compiler.parse("label:with bits\n0 0 0 0\nlabel:with bytes");

        let mut labels = HashMap::new();
        labels.insert("label".to_string(), 4);

        assert_eq!(compiler.labels, labels);
    }

    #[test]
    fn it_parses_hex_for_opcode_0() {
        let mut compiler = Compiler::new();
        compiler.parse("0 0 0 0");

        assert_eq!(compiler.bytecodes, vec![0]);
    }

    #[test]
    fn it_parses_brk_as_opcode_0() {
        let mut compiler = Compiler::new();
        compiler.parse("brk 0 0 0");

        assert_eq!(compiler.bytecodes, vec![0]);
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::path::PathBuf;

use docopt::Docopt;
use log::debug;
use rusoto_core::Region;
use serde::Deserialize;
use serde_yaml;

#[derive(Debug, Deserialize, Clone)]
pub struct Args {
    pub flag_verbose: bool,
    pub flag_quiet: bool,
    pub flag_config: PathBuf,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Config {
    pub gerrit: GerritConfig,
    pub spark: SparkConfig,
    pub bot: BotConfig,
}

#[derive(Debug, Default, Deserialize, Clone)]
pub struct GerritConfig {
    pub host: String,
    pub username: String,
    pub priv_key_path: PathBuf,
}

#[derive(Debug, Deserialize, Clone)]
pub struct SparkConfig {
    pub bot_token: String,
    pub api_uri: String,
    pub webhook_url: Option<String>,
    pub mode: ModeConfig,
    pub output_mode: OutputConfig
}

#[derive(Debug, Deserialize, Clone)]
pub enum OutputConfig {
    Spark,
    Console,
    Notifications
}

#[derive(Debug, Deserialize, Clone)]
pub enum ModeConfig {
    Direct { endpoint: String },
    Sqs { uri: String, region: Region },
}

#[derive(Debug, Deserialize, Clone)]
pub struct BotConfig {
    pub msg_expiration: u64,
    pub msg_capacity: usize,
}

impl Default for BotConfig {
    fn default() -> Self {
        Self {
            msg_expiration: 60,
            msg_capacity: 100,
        }
    }
}

const USAGE: &str = "
Cisco Spark <> Gerrit Bot

Usage:
    gerritbot-rs [--verbose | --quiet] --config=<PATH>
    gerritbot-rs [--help]

    -h --help     Show this screen.
    -v --verbose  Print more
    -q --quiet    Be silent

    -c --config=<PATH>   YAML configuration file [default: config.yml]
";

pub fn parse_args() -> Args {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
    debug!("{:#?}", args);
    args
}

pub fn parse_config(path: PathBuf) -> Config {
    let file = File::open(path).unwrap_or_else(|e| {
        eprintln!("Could not open config file: {}", e);
        eprintln!("{}", USAGE);
        ::std::process::exit(1)
    });
    let config: Config = serde_yaml::from_reader(file).unwrap_or_else(|e| {
        eprintln!("Could not parse config file: {}", e);
        ::std::process::exit(2)
    });
    debug!("{:#?}", config);
    config
}
<commit_msg>unimplement default for config structs<commit_after>use std::fs::File;
use std::path::PathBuf;

use docopt::Docopt;
use log::debug;
use rusoto_core::Region;
use serde::Deserialize;
use serde_yaml;

#[derive(Debug, Deserialize, Clone)]
pub struct Args {
    pub flag_verbose: bool,
    pub flag_quiet: bool,
    pub flag_config: PathBuf,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Config {
    pub gerrit: GerritConfig,
    pub spark: SparkConfig,
    pub bot: BotConfig,
}

#[derive(Debug, Deserialize, Clone)]
pub struct GerritConfig {
    pub host: String,
    pub username: String,
    pub priv_key_path: PathBuf,
}

#[derive(Debug, Deserialize, Clone)]
pub struct SparkConfig {
    pub bot_token: String,
    pub api_uri: String,
    pub webhook_url: Option<String>,
    pub mode: ModeConfig,
    pub output_mode: OutputConfig
}

#[derive(Debug, Deserialize, Clone)]
pub enum OutputConfig {
    Spark,
    Console,
    Notifications
}

#[derive(Debug, Deserialize, Clone)]
pub enum ModeConfig {
    Direct { endpoint: String },
    Sqs { uri: String, region: Region },
}

#[derive(Debug, Deserialize, Clone)]
pub struct BotConfig {
    pub msg_expiration: u64,
    pub msg_capacity: usize,
}

const USAGE: &str = "
Cisco Spark <> Gerrit Bot

Usage:
    gerritbot-rs [--verbose | --quiet] --config=<PATH>
    gerritbot-rs [--help]

    -h --help     Show this screen.
    -v --verbose  Print more
    -q --quiet    Be silent

    -c --config=<PATH>   YAML configuration file [default: config.yml]
";

pub fn parse_args() -> Args {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());
    debug!("{:#?}", args);
    args
}

pub fn parse_config(path: PathBuf) -> Config {
    let file = File::open(path).unwrap_or_else(|e| {
        eprintln!("Could not open config file: {}", e);
        eprintln!("{}", USAGE);
        ::std::process::exit(1)
    });
    let config: Config = serde_yaml::from_reader(file).unwrap_or_else(|e| {
        eprintln!("Could not parse config file: {}", e);
        ::std::process::exit(2)
    });
    debug!("{:#?}", config);
    config
}
<|endoftext|>


<commit_before>extern crate getopts;

use self::getopts::Options;
use crate::command;
use crate::envvar;
use crate::errors::Result;
use crate::git;

fn convert_ssh_url(mut url: String) -> String {
    if url.starts_with("git@") {
        // Note: Convert SSH protocol URL
        //  git@service.com:user/repo.git -> ssh://git@service.com:22/user/repo.git
        if let Some(i) = url.find(':') {
            url.insert_str(i + 1, "22/");
        }
        url.insert_str(0, "ssh://");
    }
    url
}

#[derive(Debug)]
pub enum ParsedArgv {
    Help(String),
    Version(&'static str),
    Parsed(command::Config),
}

fn normalize_repo_format(mut s: String, git: &git::Git) -> Result<String> {
    if let Ok(url) = git.remote_url(&s) {
        return Ok(url);
    }

    if !s.ends_with(".git") {
        s.push_str(".git");
    }

    if s.starts_with("git@") || s.starts_with("https://") || s.starts_with("http://") {
        return Ok(s);
    }

    match s.chars().filter(|c| *c == '/').count() {
        1 => Ok(format!("https://github.com/{}", s)),
        2 => Ok(format!("https://{}", s)),
        _ => Err(format!("Error: Invalid repository format '{}'. Format must be one of 'user/repo', 'service/user/repo' or remote name or Git URL.", s)),
    }
}

fn usage(program: &str) -> String {
    format!(
        "\
Usage: {} [Options] {{Args}}

  Open a repository, file, commit or diff in your web browser from command line.
  GitHub, Bitbucket, GitLab, GitHub Enterprise are supported as hosting service.
  Please see https://github.com/rhysd/git-brws for more detail.

Examples:
  - Open current repository:

    $ git brws

  - Open a file:

    $ git brws some/file.txt

  - Open specific commit:

    $ git brws HEAD~3

  - Open diff between commits:

    $ git brws HEAD~3..HEAD

  - Open line 123 of file:

    $ git brws some/file.txt#L123",
        program
    )
}

pub fn parse_options<T: AsRef<str>>(argv: &[T]) -> Result<ParsedArgv> {
    let program = argv[0].as_ref().to_owned();
    let mut opts = Options::new();

    opts.optopt("r", "repo", "Shorthand format (user/repo, service/user/repo) or remote name (e.g. origin) or Git URL you want to see", "REPO");
    opts.optopt("b", "branch", "Branch name to browse", "BRANCH");
    opts.optopt("d", "dir", "Directory path to the repository", "PATH");
    opts.optflag(
        "u",
        "url",
        "Output URL to STDOUT instead of opening in browser",
    );
    opts.optflag("h", "help", "Print this help");
    opts.optflag("v", "version", "Show version");

    let matches = opts
        .parse(argv[1..].iter().map(|a| a.as_ref()))
        .map_err(|f| format!("{}", f))?;

    if matches.opt_present("h") {
        let brief = usage(&program);
        return Ok(ParsedArgv::Help(opts.usage(&brief)));
    }

    if matches.opt_present("v") {
        return Ok(ParsedArgv::Version(
            option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
        ));
    }

    let env = envvar::new();
    let git_dir = git::git_dir(matches.opt_str("d"), env.git_command.as_str())?;
    let branch = matches.opt_str("b");
    let repo = {
        // Create scope for borrowing git_dir ref
        let git = git::new(&git_dir, env.git_command.as_str())?;
        match matches.opt_str("r") {
            Some(r) => normalize_repo_format(r, &git)?,
            None => git.tracking_remote(&branch)?,
        }
    };

    let repo = convert_ssh_url(repo);

    let stdout = matches.opt_present("u");

    Ok(ParsedArgv::Parsed(command::Config {
        repo,
        branch,
        git_dir,
        args: matches.free,
        stdout,
        env,
    }))
}
<commit_msg>fix program name in help text<commit_after>extern crate getopts;

use self::getopts::Options;
use crate::command;
use crate::envvar;
use crate::errors::Result;
use crate::git;

fn convert_ssh_url(mut url: String) -> String {
    if url.starts_with("git@") {
        // Note: Convert SSH protocol URL
        //  git@service.com:user/repo.git -> ssh://git@service.com:22/user/repo.git
        if let Some(i) = url.find(':') {
            url.insert_str(i + 1, "22/");
        }
        url.insert_str(0, "ssh://");
    }
    url
}

#[derive(Debug)]
pub enum ParsedArgv {
    Help(String),
    Version(&'static str),
    Parsed(command::Config),
}

fn normalize_repo_format(mut s: String, git: &git::Git) -> Result<String> {
    if let Ok(url) = git.remote_url(&s) {
        return Ok(url);
    }

    if !s.ends_with(".git") {
        s.push_str(".git");
    }

    if s.starts_with("git@") || s.starts_with("https://") || s.starts_with("http://") {
        return Ok(s);
    }

    match s.chars().filter(|c| *c == '/').count() {
        1 => Ok(format!("https://github.com/{}", s)),
        2 => Ok(format!("https://{}", s)),
        _ => Err(format!("Error: Invalid repository format '{}'. Format must be one of 'user/repo', 'service/user/repo' or remote name or Git URL.", s)),
    }
}

const USAGE: &'static str = "\
Usage: git brws [Options] {Args}

  Open a repository, file, commit or diff in your web browser from command line.
  GitHub, Bitbucket, GitLab, GitHub Enterprise are supported as hosting service.
  Please see https://github.com/rhysd/git-brws for more detail.

Examples:
  - Open current repository:

    $ git brws

  - Open a file:

    $ git brws some/file.txt

  - Open specific commit:

    $ git brws HEAD~3

  - Open diff between commits:

    $ git brws HEAD~3..HEAD

  - Open line 123 of file:

    $ git brws some/file.txt#L123";

pub fn parse_options<T: AsRef<str>>(argv: &[T]) -> Result<ParsedArgv> {
    let mut opts = Options::new();

    opts.optopt("r", "repo", "Shorthand format (user/repo, service/user/repo) or remote name (e.g. origin) or Git URL you want to see", "REPO");
    opts.optopt("b", "branch", "Branch name to browse", "BRANCH");
    opts.optopt("d", "dir", "Directory path to the repository", "PATH");
    opts.optflag(
        "u",
        "url",
        "Output URL to STDOUT instead of opening in browser",
    );
    opts.optflag("h", "help", "Print this help");
    opts.optflag("v", "version", "Show version");

    let matches = opts
        .parse(argv[1..].iter().map(|a| a.as_ref()))
        .map_err(|f| format!("{}", f))?;

    if matches.opt_present("h") {
        return Ok(ParsedArgv::Help(opts.usage(USAGE)));
    }

    if matches.opt_present("v") {
        return Ok(ParsedArgv::Version(
            option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
        ));
    }

    let env = envvar::new();
    let git_dir = git::git_dir(matches.opt_str("d"), env.git_command.as_str())?;
    let branch = matches.opt_str("b");
    let repo = {
        // Create scope for borrowing git_dir ref
        let git = git::new(&git_dir, env.git_command.as_str())?;
        match matches.opt_str("r") {
            Some(r) => normalize_repo_format(r, &git)?,
            None => git.tracking_remote(&branch)?,
        }
    };

    let repo = convert_ssh_url(repo);

    let stdout = matches.opt_present("u");

    Ok(ParsedArgv::Parsed(command::Config {
        repo,
        branch,
        git_dir,
        args: matches.free,
        stdout,
        env,
    }))
}
<|endoftext|>


<commit_before>use super::*;

/// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Attribute {
    pub value: MetaItem,
    pub is_sugared_doc: bool,
}

/// A compile-time attribute item.
///
/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum MetaItem {
    /// Word meta item.
    ///
    /// E.g. `test` as in `#[test]`
    Word(Ident),
    /// List meta item.
    ///
    /// E.g. `derive(..)` as in `#[derive(..)]`
    List(Ident, Vec<MetaItem>),
    /// Name value meta item.
    ///
    /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
    NameValue(Ident, Lit),
}

impl MetaItem {
    pub fn name(&self) -> &str {
        match *self {
            MetaItem::Word(ref name) => name.as_ref(),
            MetaItem::List(ref name, _) => name.as_ref(),
            MetaItem::NameValue(ref name, _) => name.as_ref(),
        }
    }
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use ident::parsing::ident;
    use lit::{Lit, StrStyle};
    use lit::parsing::lit;
    use nom::multispace;

    named!(pub attribute -> Attribute, alt!(
        do_parse!(
            punct!("#") >>
            punct!("[") >>
            meta_item: meta_item >>
            punct!("]") >>
            (Attribute {
                value: meta_item,
                is_sugared_doc: false,
            })
        )
        |
        do_parse!(
            punct!("///") >>
            space: multispace >>
            content: take_until!("\n") >>
            (Attribute {
                value: MetaItem::NameValue(
                    "doc".into(),
                    Lit::Str(
                        format!("///{}{}", space, content),
                        StrStyle::Cooked,
                    ),
                ),
                is_sugared_doc: true,
            })
        )
    ));

    named!(meta_item -> MetaItem, alt!(
        do_parse!(
            id: ident >>
            punct!("(") >>
            inner: separated_list!(punct!(","), meta_item) >>
            punct!(")") >>
            (MetaItem::List(id, inner))
        )
        |
        do_parse!(
            name: ident >>
            punct!("=") >>
            value: lit >>
            (MetaItem::NameValue(name, value))
        )
        |
        map!(ident, MetaItem::Word)
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};

    impl ToTokens for Attribute {
        fn to_tokens(&self, tokens: &mut Tokens) {
            tokens.append("#");
            tokens.append("[");
            self.value.to_tokens(tokens);
            tokens.append("]");
        }
    }

    impl ToTokens for MetaItem {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                MetaItem::Word(ref ident) => {
                    ident.to_tokens(tokens);
                }
                MetaItem::List(ref ident, ref inner) => {
                    ident.to_tokens(tokens);
                    tokens.append("(");
                    tokens.append_separated(inner, ",");
                    tokens.append(")");
                }
                MetaItem::NameValue(ref name, ref value) => {
                    name.to_tokens(tokens);
                    tokens.append("=");
                    value.to_tokens(tokens);
                }
            }
        }
    }
}
<commit_msg>Preserve sugared doc comments<commit_after>use super::*;

/// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Attribute {
    pub value: MetaItem,
    pub is_sugared_doc: bool,
}

/// A compile-time attribute item.
///
/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum MetaItem {
    /// Word meta item.
    ///
    /// E.g. `test` as in `#[test]`
    Word(Ident),
    /// List meta item.
    ///
    /// E.g. `derive(..)` as in `#[derive(..)]`
    List(Ident, Vec<MetaItem>),
    /// Name value meta item.
    ///
    /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
    NameValue(Ident, Lit),
}

impl MetaItem {
    pub fn name(&self) -> &str {
        match *self {
            MetaItem::Word(ref name) => name.as_ref(),
            MetaItem::List(ref name, _) => name.as_ref(),
            MetaItem::NameValue(ref name, _) => name.as_ref(),
        }
    }
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use ident::parsing::ident;
    use lit::{Lit, StrStyle};
    use lit::parsing::lit;

    named!(pub attribute -> Attribute, alt!(
        do_parse!(
            punct!("#") >>
            punct!("[") >>
            meta_item: meta_item >>
            punct!("]") >>
            (Attribute {
                value: meta_item,
                is_sugared_doc: false,
            })
        )
        |
        do_parse!(
            punct!("///") >>
            not!(peek!(tag!("/"))) >>
            content: take_until!("\n") >>
            (Attribute {
                value: MetaItem::NameValue(
                    "doc".into(),
                    Lit::Str(
                        format!("///{}", content),
                        StrStyle::Cooked,
                    ),
                ),
                is_sugared_doc: true,
            })
        )
    ));

    named!(meta_item -> MetaItem, alt!(
        do_parse!(
            id: ident >>
            punct!("(") >>
            inner: separated_list!(punct!(","), meta_item) >>
            punct!(")") >>
            (MetaItem::List(id, inner))
        )
        |
        do_parse!(
            name: ident >>
            punct!("=") >>
            value: lit >>
            (MetaItem::NameValue(name, value))
        )
        |
        map!(ident, MetaItem::Word)
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use lit::{Lit, StrStyle};
    use quote::{Tokens, ToTokens};

    impl ToTokens for Attribute {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                Attribute {
                    value: MetaItem::NameValue(
                        ref name,
                        Lit::Str(ref value, StrStyle::Cooked),
                    ),
                    is_sugared_doc: true,
                } if name == "doc" && value.starts_with("///") => {
                    tokens.append(&format!("{}\n", value));
                }
                _ => {
                    tokens.append("#");
                    tokens.append("[");
                    self.value.to_tokens(tokens);
                    tokens.append("]");
                }
            }
        }
    }

    impl ToTokens for MetaItem {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                MetaItem::Word(ref ident) => {
                    ident.to_tokens(tokens);
                }
                MetaItem::List(ref ident, ref inner) => {
                    ident.to_tokens(tokens);
                    tokens.append("(");
                    tokens.append_separated(inner, ",");
                    tokens.append(")");
                }
                MetaItem::NameValue(ref name, ref value) => {
                    name.to_tokens(tokens);
                    tokens.append("=");
                    value.to_tokens(tokens);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::fmt;
use std::io::{self, Cursor, Read};

use bytes::Bytes;
use futures::Future;
use hyper::{self};

use {async_impl};

/// The body of a `Request`.
///
/// In most cases, this is not needed directly, as the
/// [`RequestBuilder.body`][builder] method uses `Into<Body>`, which allows
/// passing many things (like a string or vector of bytes).
///
/// [builder]: ./struct.RequestBuilder.html#method.body
#[derive(Debug)]
pub struct Body {
    kind: Kind,
}

impl Body {
    /// Instantiate a `Body` from a reader.
    ///
    /// # Note
    ///
    /// While allowing for many types to be used, these bodies do not have
    /// a way to reset to the beginning and be reused. This means that when
    /// encountering a 307 or 308 status code, instead of repeating the
    /// request at the new location, the `Response` will be returned with
    /// the redirect status code set.
    ///
    /// ```rust
    /// # use std::fs::File;
    /// # use reqwest::Body;
    /// # fn run() -> Result<(), Box<std::error::Error>> {
    /// let file = File::open("national_secrets.txt")?;
    /// let body = Body::new(file);
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// If you have a set of bytes, like `String` or `Vec<u8>`, using the
    /// `From` implementations for `Body` will store the data in a manner
    /// it can be reused.
    ///
    /// ```rust
    /// # use reqwest::Body;
    /// # fn run() -> Result<(), Box<std::error::Error>> {
    /// let s = "A stringy body";
    /// let body = Body::from(s);
    /// # Ok(())
    /// # }
    /// ```
    pub fn new<R: Read + Send + 'static>(reader: R) -> Body {
        Body {
            kind: Kind::Reader(Box::from(reader), None),
        }
    }

    /// Create a `Body` from a `Read` where the size is known in advance
    /// but the data should not be fully loaded into memory. This will
    /// set the `Content-Length` header and stream from the `Read`.
    ///
    /// ```rust
    /// # use std::fs::File;
    /// # use reqwest::Body;
    /// # fn run() -> Result<(), Box<std::error::Error>> {
    /// let file = File::open("a_large_file.txt")?;
    /// let file_size = file.metadata()?.len();
    /// let body = Body::sized(file, file_size);
    /// # Ok(())
    /// # }
    /// ```
    pub fn sized<R: Read + Send + 'static>(reader: R, len: u64) -> Body {
        Body {
            kind: Kind::Reader(Box::from(reader), Some(len)),
        }
    }

    pub(crate) fn len(&self) -> Option<u64> {
        match self.kind {
            Kind::Reader(_, len) => len,
            Kind::Bytes(ref bytes) => Some(bytes.len() as u64),
        }
    }

    pub(crate) fn into_reader(self) -> Reader {
        match self.kind {
            Kind::Reader(r, _) => Reader::Reader(r),
            Kind::Bytes(b) => Reader::Bytes(Cursor::new(b)),
        }
    }

    pub(crate) fn into_async(self) -> (Option<Sender>, async_impl::Body, Option<u64>) {
        match self.kind {
            Kind::Reader(read, len) => {
                let (tx, rx) = hyper::Body::channel();
                let tx = Sender {
                    body: (read, len),
                    tx: tx,
                };
                (Some(tx), async_impl::Body::wrap(rx), len)
            },
            Kind::Bytes(chunk) => {
                let len = chunk.len() as u64;
                (None, async_impl::Body::reusable(chunk), Some(len))
            }
        }
    }

    pub(crate) fn try_clone(&self) -> Option<Body> {
        self.kind.try_clone()
            .map(|kind| Body { kind })
    }
}

enum Kind {
    Reader(Box<Read + Send>, Option<u64>),
    Bytes(Bytes),
}

impl Kind {
    fn try_clone(&self) -> Option<Kind> {
        match self {
            Kind::Reader(..) => None,
            Kind::Bytes(v) => Some(Kind::Bytes(v.clone())),
        }
    }
}

impl From<Vec<u8>> for Body {
    #[inline]
    fn from(v: Vec<u8>) -> Body {
        Body {
            kind: Kind::Bytes(v.into()),
        }
    }
}

impl From<String> for Body {
    #[inline]
    fn from(s: String) -> Body {
        s.into_bytes().into()
    }
}

impl From<&'static [u8]> for Body {
    #[inline]
    fn from(s: &'static [u8]) -> Body {
        Body {
            kind: Kind::Bytes(Bytes::from_static(s)),
        }
    }
}

impl From<&'static str> for Body {
    #[inline]
    fn from(s: &'static str) -> Body {
        s.as_bytes().into()
    }
}

impl From<File> for Body {
    #[inline]
    fn from(f: File) -> Body {
        let len = f.metadata().map(|m| m.len()).ok();
        Body {
            kind: Kind::Reader(Box::new(f), len),
        }
    }
}

impl fmt::Debug for Kind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Kind::Reader(_, ref v) => f.debug_struct("Reader")
                .field("length", &DebugLength(v))
                .finish(),
            Kind::Bytes(ref v) => fmt::Debug::fmt(v, f),
        }
    }
}

struct DebugLength<'a>(&'a Option<u64>);

impl<'a> fmt::Debug for DebugLength<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self.0 {
            Some(ref len) => fmt::Debug::fmt(len, f),
            None => f.write_str("Unknown"),
        }
    }
}

pub(crate) enum Reader {
    Reader(Box<Read + Send>),
    Bytes(Cursor<Bytes>),
}

impl Read for Reader {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Reader::Reader(ref mut rdr) => rdr.read(buf),
            Reader::Bytes(ref mut rdr) => rdr.read(buf),
        }
    }
}

pub(crate) struct Sender {
    body: (Box<Read + Send>, Option<u64>),
    tx: hyper::body::Sender,
}

impl Sender {
    // A `Future` that may do blocking read calls.
    // As a `Future`, this integrates easily with `wait::timeout`.
    pub(crate) fn send(self) -> impl Future<Item=(), Error=::Error> {
        use std::cmp;
        use bytes::{BufMut, BytesMut};
        use futures::future;

        let con_len = self.body.1;
        let cap = cmp::min(self.body.1.unwrap_or(8192), 8192);
        let mut written = 0;
        let mut buf = BytesMut::with_capacity(cap as usize);
        let mut body = self.body.0;
        // Put in an option so that it can be consumed on error to call abort()
        let mut tx = Some(self.tx);

        future::poll_fn(move || loop {
            if Some(written) == con_len {
                // Written up to content-length, so stop.
                return Ok(().into());
            }

            try_ready!(tx
                .as_mut()
                .expect("tx only taken on error")
                .poll_ready()
                .map_err(::error::from));

            if buf.remaining_mut() == 0 {
                buf.reserve(8192);
            }

            match body.read(unsafe { buf.bytes_mut() }) {
                Ok(0) => {
                    return Ok(().into())
                },
                Ok(n) => {
                    unsafe { buf.advance_mut(n); }
                    written += n as u64;
                    let tx = tx.as_mut().expect("tx only taken on error");
                    if let Err(_) = tx.send_data(buf.take().freeze().into()) {
                        return Err(::error::timedout(None));
                    }
                }
                Err(e) => {
                    let ret = io::Error::new(e.kind(), e.to_string());
                    tx
                        .take()
                        .expect("tx only taken on error")
                        .abort();
                    return Err(::error::from(ret));
                }
            }
        })
    }
}

// useful for tests, but not publicly exposed
#[cfg(test)]
pub(crate) fn read_to_string(mut body: Body) -> io::Result<String> {
    let mut s = String::new();
    match body.kind {
            Kind::Reader(ref mut reader, _) => reader.read_to_string(&mut s),
            Kind::Bytes(ref mut bytes) => (&**bytes).read_to_string(&mut s),
        }
        .map(|_| s)
}
<commit_msg>Body: don't call poll_ready on tx when 0 bytes remaining. (#479)<commit_after>use std::fs::File;
use std::fmt;
use std::io::{self, Cursor, Read};

use bytes::Bytes;
use futures::Future;
use hyper::{self};

use {async_impl};

/// The body of a `Request`.
///
/// In most cases, this is not needed directly, as the
/// [`RequestBuilder.body`][builder] method uses `Into<Body>`, which allows
/// passing many things (like a string or vector of bytes).
///
/// [builder]: ./struct.RequestBuilder.html#method.body
#[derive(Debug)]
pub struct Body {
    kind: Kind,
}

impl Body {
    /// Instantiate a `Body` from a reader.
    ///
    /// # Note
    ///
    /// While allowing for many types to be used, these bodies do not have
    /// a way to reset to the beginning and be reused. This means that when
    /// encountering a 307 or 308 status code, instead of repeating the
    /// request at the new location, the `Response` will be returned with
    /// the redirect status code set.
    ///
    /// ```rust
    /// # use std::fs::File;
    /// # use reqwest::Body;
    /// # fn run() -> Result<(), Box<std::error::Error>> {
    /// let file = File::open("national_secrets.txt")?;
    /// let body = Body::new(file);
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// If you have a set of bytes, like `String` or `Vec<u8>`, using the
    /// `From` implementations for `Body` will store the data in a manner
    /// it can be reused.
    ///
    /// ```rust
    /// # use reqwest::Body;
    /// # fn run() -> Result<(), Box<std::error::Error>> {
    /// let s = "A stringy body";
    /// let body = Body::from(s);
    /// # Ok(())
    /// # }
    /// ```
    pub fn new<R: Read + Send + 'static>(reader: R) -> Body {
        Body {
            kind: Kind::Reader(Box::from(reader), None),
        }
    }

    /// Create a `Body` from a `Read` where the size is known in advance
    /// but the data should not be fully loaded into memory. This will
    /// set the `Content-Length` header and stream from the `Read`.
    ///
    /// ```rust
    /// # use std::fs::File;
    /// # use reqwest::Body;
    /// # fn run() -> Result<(), Box<std::error::Error>> {
    /// let file = File::open("a_large_file.txt")?;
    /// let file_size = file.metadata()?.len();
    /// let body = Body::sized(file, file_size);
    /// # Ok(())
    /// # }
    /// ```
    pub fn sized<R: Read + Send + 'static>(reader: R, len: u64) -> Body {
        Body {
            kind: Kind::Reader(Box::from(reader), Some(len)),
        }
    }

    pub(crate) fn len(&self) -> Option<u64> {
        match self.kind {
            Kind::Reader(_, len) => len,
            Kind::Bytes(ref bytes) => Some(bytes.len() as u64),
        }
    }

    pub(crate) fn into_reader(self) -> Reader {
        match self.kind {
            Kind::Reader(r, _) => Reader::Reader(r),
            Kind::Bytes(b) => Reader::Bytes(Cursor::new(b)),
        }
    }

    pub(crate) fn into_async(self) -> (Option<Sender>, async_impl::Body, Option<u64>) {
        match self.kind {
            Kind::Reader(read, len) => {
                let (tx, rx) = hyper::Body::channel();
                let tx = Sender {
                    body: (read, len),
                    tx: tx,
                };
                (Some(tx), async_impl::Body::wrap(rx), len)
            },
            Kind::Bytes(chunk) => {
                let len = chunk.len() as u64;
                (None, async_impl::Body::reusable(chunk), Some(len))
            }
        }
    }

    pub(crate) fn try_clone(&self) -> Option<Body> {
        self.kind.try_clone()
            .map(|kind| Body { kind })
    }
}

enum Kind {
    Reader(Box<Read + Send>, Option<u64>),
    Bytes(Bytes),
}

impl Kind {
    fn try_clone(&self) -> Option<Kind> {
        match self {
            Kind::Reader(..) => None,
            Kind::Bytes(v) => Some(Kind::Bytes(v.clone())),
        }
    }
}

impl From<Vec<u8>> for Body {
    #[inline]
    fn from(v: Vec<u8>) -> Body {
        Body {
            kind: Kind::Bytes(v.into()),
        }
    }
}

impl From<String> for Body {
    #[inline]
    fn from(s: String) -> Body {
        s.into_bytes().into()
    }
}

impl From<&'static [u8]> for Body {
    #[inline]
    fn from(s: &'static [u8]) -> Body {
        Body {
            kind: Kind::Bytes(Bytes::from_static(s)),
        }
    }
}

impl From<&'static str> for Body {
    #[inline]
    fn from(s: &'static str) -> Body {
        s.as_bytes().into()
    }
}

impl From<File> for Body {
    #[inline]
    fn from(f: File) -> Body {
        let len = f.metadata().map(|m| m.len()).ok();
        Body {
            kind: Kind::Reader(Box::new(f), len),
        }
    }
}

impl fmt::Debug for Kind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Kind::Reader(_, ref v) => f.debug_struct("Reader")
                .field("length", &DebugLength(v))
                .finish(),
            Kind::Bytes(ref v) => fmt::Debug::fmt(v, f),
        }
    }
}

struct DebugLength<'a>(&'a Option<u64>);

impl<'a> fmt::Debug for DebugLength<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self.0 {
            Some(ref len) => fmt::Debug::fmt(len, f),
            None => f.write_str("Unknown"),
        }
    }
}

pub(crate) enum Reader {
    Reader(Box<Read + Send>),
    Bytes(Cursor<Bytes>),
}

impl Read for Reader {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Reader::Reader(ref mut rdr) => rdr.read(buf),
            Reader::Bytes(ref mut rdr) => rdr.read(buf),
        }
    }
}

pub(crate) struct Sender {
    body: (Box<Read + Send>, Option<u64>),
    tx: hyper::body::Sender,
}

impl Sender {
    // A `Future` that may do blocking read calls.
    // As a `Future`, this integrates easily with `wait::timeout`.
    pub(crate) fn send(self) -> impl Future<Item=(), Error=::Error> {
        use std::cmp;
        use bytes::{BufMut, BytesMut};
        use futures::future;

        let con_len = self.body.1;
        let cap = cmp::min(self.body.1.unwrap_or(8192), 8192);
        let mut written = 0;
        let mut buf = BytesMut::with_capacity(cap as usize);
        let mut body = self.body.0;
        // Put in an option so that it can be consumed on error to call abort()
        let mut tx = Some(self.tx);

        future::poll_fn(move || loop {
            if Some(written) == con_len {
                // Written up to content-length, so stop.
                return Ok(().into());
            }

            // The input stream is read only if the buffer is empty so
            // that there is only one read in the buffer at any time.
            //
            // We need to know whether there is any data to send before
            // we check the transmission channel (with poll_ready below)
            // because somestimes the receiver disappears as soon as is
            // considers the data is completely transmitted, which may
            // be true.
            //
            // The use case is a web server that closes its
            // input stream as soon as the data received is valid JSON.
            // This behaviour is questionable, but it exists and the
            // fact is that there is actually no remaining data to read.
            if buf.len() == 0 {
                if buf.remaining_mut() == 0 {
                    buf.reserve(8192);
                }

                match body.read(unsafe { buf.bytes_mut() }) {
                    Ok(0) => {
                        // The buffer was empty and nothing's left to
                        // read. Return.
                        return Ok(().into());
                    }
                    Ok(n) => {
                        unsafe { buf.advance_mut(n); }
                    }
                    Err(e) => {
                        let ret = io::Error::new(e.kind(), e.to_string());
                        tx
                            .take()
                            .expect("tx only taken on error")
                            .abort();
                        return Err(::error::from(ret));
                    }
                }
            }

            // The only way to get here is when the buffer is not empty.
            // We can check the transmission channel
            try_ready!(tx
                .as_mut()
                .expect("tx only taken on error")
                .poll_ready()
                .map_err(::error::from));

            written += buf.len() as u64;
            let tx = tx.as_mut().expect("tx only taken on error");
            if let Err(_) = tx.send_data(buf.take().freeze().into()) {
                return Err(::error::timedout(None));
            }
        })
    }
}

// useful for tests, but not publicly exposed
#[cfg(test)]
pub(crate) fn read_to_string(mut body: Body) -> io::Result<String> {
    let mut s = String::new();
    match body.kind {
            Kind::Reader(ref mut reader, _) => reader.read_to_string(&mut s),
            Kind::Bytes(ref mut bytes) => (&**bytes).read_to_string(&mut s),
        }
        .map(|_| s)
}
<|endoftext|>


<commit_before>//! C API for mp4parse module.
//!
//! Parses ISO Base Media Format aka video/mp4 streams.
//!
//! # Examples
//!
//! ```rust
//! extern crate mp4parse;
//!
//! // Minimal valid mp4 containing no tracks.
//! let data = b"\0\0\0\x08moov";
//!
//! let context = mp4parse::mp4parse_new();
//! unsafe {
//!     let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
//!     assert_eq!(rv, mp4parse::mp4parse_error::MP4PARSE_OK);
//!     mp4parse::mp4parse_free(context);
//! }
//! ```

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

use std;
use std::io::Cursor;

// Symbols we need from our rust api.
use MediaContext;
use TrackType;
use read_mp4;
use Error;
use media_time_to_ms;
use track_time_to_ms;
use SampleEntry;

// rusty-cheddar's C enum generation doesn't namespace enum members by
// prefixing them, so we're forced to do it in our member names until
// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed.  Importing
// the members into the module namespace avoids doubling up on the
// namespacing on the Rust side.
use mp4parse_error::*;
use mp4parse_track_type::*;

#[repr(C)]
#[derive(PartialEq,Debug)]
pub enum mp4parse_error {
    MP4PARSE_OK = 0,
    MP4PARSE_ERROR_BADARG = 1,
    MP4PARSE_ERROR_INVALID = 2,
    MP4PARSE_ERROR_UNSUPPORTED = 3,
    MP4PARSE_ERROR_EOF = 4,
    MP4PARSE_ERROR_ASSERT = 5,
    MP4PARSE_ERROR_IO = 6,
}

#[repr(C)]
pub enum mp4parse_track_type {
    MP4PARSE_TRACK_TYPE_VIDEO = 0,
    MP4PARSE_TRACK_TYPE_AUDIO = 1,
}

#[repr(C)]
pub struct mp4parse_track_info {
    pub track_type: mp4parse_track_type,
    pub track_id: u32,
    pub duration: u64,
    pub media_time: i64, // wants to be u64? understand how elst adjustment works
    // TODO(kinetik): include crypto guff
}

#[repr(C)]
pub struct mp4parse_track_audio_info {
    pub channels: u16,
    pub bit_depth: u16,
    pub sample_rate: u32,
    // TODO(kinetik):
    // int32_t profile;
    // int32_t extended_profile; // check types
    // extra_data
    // codec_specific_config
}

#[repr(C)]
pub struct mp4parse_track_video_info {
    pub display_width: u32,
    pub display_height: u32,
    pub image_width: u16,
    pub image_height: u16,
    // TODO(kinetik):
    // extra_data
    // codec_specific_config
}

#[repr(C)]
#[allow(non_camel_case_types)]
pub struct mp4parse_state(MediaContext);

// C API wrapper functions.

/// Allocate an opaque rust-side parser context.
#[no_mangle]
pub extern "C" fn mp4parse_new() -> *mut mp4parse_state {
    let context = Box::new(mp4parse_state(MediaContext::new()));
    Box::into_raw(context)
}

/// Free a rust-side parser context.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_free(context: *mut mp4parse_state) {
    assert!(!context.is_null());
    let _ = Box::from_raw(context);
}

/// Feed a buffer through `read_mp4()` with the given rust-side
/// parser context, returning success or an error code.
///
/// This is safe to call with NULL arguments but will crash
/// if given invalid pointers, as is usual for C.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_read(context: *mut mp4parse_state, buffer: *const u8, size: usize) -> mp4parse_error {
    // Validate arguments from C.
    if context.is_null() || buffer.is_null() || size < 8 {
        return MP4PARSE_ERROR_BADARG;
    }

    let mut context: &mut MediaContext = &mut (*context).0;

    // Wrap the buffer we've been give in a slice.
    let b = std::slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    let r = if cfg!(not(feature = "fuzz")) {
        // Parse in a subthread to catch any panics.
        let task = std::thread::spawn(move || read_mp4(&mut c, &mut context));
        // The task's JoinHandle will return an error result if the thread
        // panicked, and will wrap the closure's return'd result in an
        // Ok(..) otherwise, meaning we could see Ok(Err(Error::..))
        // here. So map thread failures back to an
        // mp4parse::Error::AssertCaught.
        task.join().unwrap_or(Err(Error::AssertCaught))
    } else {
        read_mp4(&mut c, &mut context)
    };
    match r {
        Ok(_) => MP4PARSE_OK,
        Err(Error::NoMoov) => MP4PARSE_ERROR_INVALID,
        Err(Error::InvalidData(_)) => MP4PARSE_ERROR_INVALID,
        Err(Error::Unsupported(_)) => MP4PARSE_ERROR_UNSUPPORTED,
        Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF,
        Err(Error::AssertCaught) => MP4PARSE_ERROR_ASSERT,
        Err(Error::Io(_)) => MP4PARSE_ERROR_IO,
    }
}

/// Return the number of tracks parsed by previous `read_mp4()` calls.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_count(context: *const mp4parse_state) -> u32 {
    // Validate argument from C.
    assert!(!context.is_null());
    let context: &MediaContext = &(*context).0;

    // Make sure the track count fits in a u32.
    assert!(context.tracks.len() < u32::max_value() as usize);
    context.tracks.len() as u32
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;
    let track_index: usize = track as usize;
    let info: &mut mp4parse_track_info = &mut *info;

    if track_index >= context.tracks.len() {
        return MP4PARSE_ERROR_BADARG;
    }

    info.track_type = match context.tracks[track_index].track_type {
        TrackType::Video => MP4PARSE_TRACK_TYPE_VIDEO,
        TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO,
        TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED,
    };

    // Maybe context & track should just have a single simple is_valid() instead?
    if context.timescale.is_none() ||
       context.tracks[track_index].timescale.is_none() ||
       context.tracks[track_index].duration.is_none() ||
       context.tracks[track_index].track_id.is_none() {
        return MP4PARSE_ERROR_INVALID;
    }

    let track = &context.tracks[track_index];
    info.media_time = track.media_time.map_or(0, |media_time| {
        track_time_to_ms(media_time, track.timescale.unwrap()) as i64
    }) - track.empty_duration.map_or(0, |empty_duration| {
        media_time_to_ms(empty_duration, context.timescale.unwrap()) as i64
    });
    info.duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap());
    info.track_id = track.track_id.unwrap();

    MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Audio => {}
        _ => return MP4PARSE_ERROR_INVALID,
    };

    let audio = match track.data {
        Some(ref data) => data,
        None => return MP4PARSE_ERROR_INVALID,
    };

    let audio = match *audio {
        SampleEntry::Audio(ref x) => x,
        _ => return MP4PARSE_ERROR_INVALID,
    };

    (*info).channels = audio.channelcount;
    (*info).bit_depth = audio.samplesize;
    (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point

    MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Video => {}
        _ => return MP4PARSE_ERROR_INVALID,
    };

    let video = match track.data {
        Some(ref data) => data,
        None => return MP4PARSE_ERROR_INVALID,
    };

    let video = match *video {
        SampleEntry::Video(ref x) => x,
        _ => return MP4PARSE_ERROR_INVALID,
    };

    if let Some(ref tkhd) = track.tkhd {
        (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
        (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
    } else {
        return MP4PARSE_ERROR_INVALID;
    }
    (*info).image_width = video.width;
    (*info).image_height = video.height;

    MP4PARSE_OK
}

#[test]
fn new_context() {
    let context = mp4parse_new();
    assert!(!context.is_null());
    unsafe {
        mp4parse_free(context);
    }
}

#[test]
#[should_panic(expected = "assertion failed")]
fn free_null_context() {
    unsafe {
        mp4parse_free(std::ptr::null_mut());
    }
}

#[test]
fn arg_validation() {
    let null_buffer = std::ptr::null();
    let null_context = std::ptr::null_mut();

    let context = mp4parse_new();
    assert!(!context.is_null());

    let buffer = vec![0u8; 8];

    unsafe {
        assert_eq!(MP4PARSE_ERROR_BADARG,
                   mp4parse_read(null_context, null_buffer, 0));
        assert_eq!(MP4PARSE_ERROR_BADARG,
                   mp4parse_read(context, null_buffer, 0));
    }

    for size in 0..buffer.len() {
        println!("testing buffer length {}", size);
        unsafe {
            assert_eq!(MP4PARSE_ERROR_BADARG,
                       mp4parse_read(context, buffer.as_ptr(), size));
        }
    }

    unsafe {
        mp4parse_free(context);
    }
}
<commit_msg>Extern uses C API by default.<commit_after>//! C API for mp4parse module.
//!
//! Parses ISO Base Media Format aka video/mp4 streams.
//!
//! # Examples
//!
//! ```rust
//! extern crate mp4parse;
//!
//! // Minimal valid mp4 containing no tracks.
//! let data = b"\0\0\0\x08moov";
//!
//! let context = mp4parse::mp4parse_new();
//! unsafe {
//!     let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
//!     assert_eq!(rv, mp4parse::mp4parse_error::MP4PARSE_OK);
//!     mp4parse::mp4parse_free(context);
//! }
//! ```

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

use std;
use std::io::Cursor;

// Symbols we need from our rust api.
use MediaContext;
use TrackType;
use read_mp4;
use Error;
use media_time_to_ms;
use track_time_to_ms;
use SampleEntry;

// rusty-cheddar's C enum generation doesn't namespace enum members by
// prefixing them, so we're forced to do it in our member names until
// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed.  Importing
// the members into the module namespace avoids doubling up on the
// namespacing on the Rust side.
use mp4parse_error::*;
use mp4parse_track_type::*;

#[repr(C)]
#[derive(PartialEq,Debug)]
pub enum mp4parse_error {
    MP4PARSE_OK = 0,
    MP4PARSE_ERROR_BADARG = 1,
    MP4PARSE_ERROR_INVALID = 2,
    MP4PARSE_ERROR_UNSUPPORTED = 3,
    MP4PARSE_ERROR_EOF = 4,
    MP4PARSE_ERROR_ASSERT = 5,
    MP4PARSE_ERROR_IO = 6,
}

#[repr(C)]
pub enum mp4parse_track_type {
    MP4PARSE_TRACK_TYPE_VIDEO = 0,
    MP4PARSE_TRACK_TYPE_AUDIO = 1,
}

#[repr(C)]
pub struct mp4parse_track_info {
    pub track_type: mp4parse_track_type,
    pub track_id: u32,
    pub duration: u64,
    pub media_time: i64, // wants to be u64? understand how elst adjustment works
    // TODO(kinetik): include crypto guff
}

#[repr(C)]
pub struct mp4parse_track_audio_info {
    pub channels: u16,
    pub bit_depth: u16,
    pub sample_rate: u32,
    // TODO(kinetik):
    // int32_t profile;
    // int32_t extended_profile; // check types
    // extra_data
    // codec_specific_config
}

#[repr(C)]
pub struct mp4parse_track_video_info {
    pub display_width: u32,
    pub display_height: u32,
    pub image_width: u16,
    pub image_height: u16,
    // TODO(kinetik):
    // extra_data
    // codec_specific_config
}

#[repr(C)]
#[allow(non_camel_case_types)]
pub struct mp4parse_state(MediaContext);

// C API wrapper functions.

/// Allocate an opaque rust-side parser context.
#[no_mangle]
pub extern fn mp4parse_new() -> *mut mp4parse_state {
    let context = Box::new(mp4parse_state(MediaContext::new()));
    Box::into_raw(context)
}

/// Free a rust-side parser context.
#[no_mangle]
pub unsafe extern fn mp4parse_free(context: *mut mp4parse_state) {
    assert!(!context.is_null());
    let _ = Box::from_raw(context);
}

/// Feed a buffer through `read_mp4()` with the given rust-side
/// parser context, returning success or an error code.
///
/// This is safe to call with NULL arguments but will crash
/// if given invalid pointers, as is usual for C.
#[no_mangle]
pub unsafe extern fn mp4parse_read(context: *mut mp4parse_state, buffer: *const u8, size: usize) -> mp4parse_error {
    // Validate arguments from C.
    if context.is_null() || buffer.is_null() || size < 8 {
        return MP4PARSE_ERROR_BADARG;
    }

    let mut context: &mut MediaContext = &mut (*context).0;

    // Wrap the buffer we've been give in a slice.
    let b = std::slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    let r = if cfg!(not(feature = "fuzz")) {
        // Parse in a subthread to catch any panics.
        let task = std::thread::spawn(move || read_mp4(&mut c, &mut context));
        // The task's JoinHandle will return an error result if the thread
        // panicked, and will wrap the closure's return'd result in an
        // Ok(..) otherwise, meaning we could see Ok(Err(Error::..))
        // here. So map thread failures back to an
        // mp4parse::Error::AssertCaught.
        task.join().unwrap_or(Err(Error::AssertCaught))
    } else {
        read_mp4(&mut c, &mut context)
    };
    match r {
        Ok(_) => MP4PARSE_OK,
        Err(Error::NoMoov) => MP4PARSE_ERROR_INVALID,
        Err(Error::InvalidData(_)) => MP4PARSE_ERROR_INVALID,
        Err(Error::Unsupported(_)) => MP4PARSE_ERROR_UNSUPPORTED,
        Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF,
        Err(Error::AssertCaught) => MP4PARSE_ERROR_ASSERT,
        Err(Error::Io(_)) => MP4PARSE_ERROR_IO,
    }
}

/// Return the number of tracks parsed by previous `read_mp4()` calls.
#[no_mangle]
pub unsafe extern fn mp4parse_get_track_count(context: *const mp4parse_state) -> u32 {
    // Validate argument from C.
    assert!(!context.is_null());
    let context: &MediaContext = &(*context).0;

    // Make sure the track count fits in a u32.
    assert!(context.tracks.len() < u32::max_value() as usize);
    context.tracks.len() as u32
}

#[no_mangle]
pub unsafe extern fn mp4parse_get_track_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;
    let track_index: usize = track as usize;
    let info: &mut mp4parse_track_info = &mut *info;

    if track_index >= context.tracks.len() {
        return MP4PARSE_ERROR_BADARG;
    }

    info.track_type = match context.tracks[track_index].track_type {
        TrackType::Video => MP4PARSE_TRACK_TYPE_VIDEO,
        TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO,
        TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED,
    };

    // Maybe context & track should just have a single simple is_valid() instead?
    if context.timescale.is_none() ||
       context.tracks[track_index].timescale.is_none() ||
       context.tracks[track_index].duration.is_none() ||
       context.tracks[track_index].track_id.is_none() {
        return MP4PARSE_ERROR_INVALID;
    }

    let track = &context.tracks[track_index];
    info.media_time = track.media_time.map_or(0, |media_time| {
        track_time_to_ms(media_time, track.timescale.unwrap()) as i64
    }) - track.empty_duration.map_or(0, |empty_duration| {
        media_time_to_ms(empty_duration, context.timescale.unwrap()) as i64
    });
    info.duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap());
    info.track_id = track.track_id.unwrap();

    MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern fn mp4parse_get_track_audio_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Audio => {}
        _ => return MP4PARSE_ERROR_INVALID,
    };

    let audio = match track.data {
        Some(ref data) => data,
        None => return MP4PARSE_ERROR_INVALID,
    };

    let audio = match *audio {
        SampleEntry::Audio(ref x) => x,
        _ => return MP4PARSE_ERROR_INVALID,
    };

    (*info).channels = audio.channelcount;
    (*info).bit_depth = audio.samplesize;
    (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point

    MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern fn mp4parse_get_track_video_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Video => {}
        _ => return MP4PARSE_ERROR_INVALID,
    };

    let video = match track.data {
        Some(ref data) => data,
        None => return MP4PARSE_ERROR_INVALID,
    };

    let video = match *video {
        SampleEntry::Video(ref x) => x,
        _ => return MP4PARSE_ERROR_INVALID,
    };

    if let Some(ref tkhd) = track.tkhd {
        (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
        (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
    } else {
        return MP4PARSE_ERROR_INVALID;
    }
    (*info).image_width = video.width;
    (*info).image_height = video.height;

    MP4PARSE_OK
}

#[test]
fn new_context() {
    let context = mp4parse_new();
    assert!(!context.is_null());
    unsafe {
        mp4parse_free(context);
    }
}

#[test]
#[should_panic(expected = "assertion failed")]
fn free_null_context() {
    unsafe {
        mp4parse_free(std::ptr::null_mut());
    }
}

#[test]
fn arg_validation() {
    let null_buffer = std::ptr::null();
    let null_context = std::ptr::null_mut();

    let context = mp4parse_new();
    assert!(!context.is_null());

    let buffer = vec![0u8; 8];

    unsafe {
        assert_eq!(MP4PARSE_ERROR_BADARG,
                   mp4parse_read(null_context, null_buffer, 0));
        assert_eq!(MP4PARSE_ERROR_BADARG,
                   mp4parse_read(context, null_buffer, 0));
    }

    for size in 0..buffer.len() {
        println!("testing buffer length {}", size);
        unsafe {
            assert_eq!(MP4PARSE_ERROR_BADARG,
                       mp4parse_read(context, buffer.as_ptr(), size));
        }
    }

    unsafe {
        mp4parse_free(context);
    }
}
<|endoftext|>


<commit_before>use activation_function::ActivationFunction;
use acyclic_network::{NodeType, Network, Link, Node};
pub use acyclic_network::NodeIndex as CppnNodeIndex;
use fixedbitset::FixedBitSet;

#[derive(Clone, Debug)]
pub enum CppnNodeType<A: ActivationFunction> {
    Bias,
    Input,
    Output,
    Hidden(A),
}

const CPPN_BIAS_WEIGHT: f64 = 1.0;

impl<A: ActivationFunction> NodeType for CppnNodeType<A> {
    fn accept_incoming_links(&self) -> bool {
        match *self {
            CppnNodeType::Input | CppnNodeType::Bias => false,
            _ => true,
        }
    }

    fn accept_outgoing_links(&self) -> bool {
        match *self {
            CppnNodeType::Output => false,
            _ => true,
        }
    }
}

pub type CppnLink = Link<f64>;
pub type CppnNode<A: ActivationFunction> = Node<CppnNodeType<A>, f64>;
pub type CppnGraph<A: ActivationFunction> = Network<CppnNodeType<A>, f64>;

/// Represents a Compositional Pattern Producing Network (CPPN)
pub struct Cppn<'a, A: ActivationFunction + 'a> {
    graph: &'a CppnGraph<A>,
    inputs: Vec<CppnNodeIndex>,
    outputs: Vec<CppnNodeIndex>,

    // For each node in `graph` there exists a corresponding field in `incoming_signals` describing
    // the sum of all input signals for that node.  We could store it inline in the `CppnNode`, but
    // this would require to make the whole CppnGraph mutable.
    incoming_signals: Vec<f64>,
}

impl<'a, A: ActivationFunction> Cppn<'a, A> {
    pub fn new(graph: &'a CppnGraph<A>) -> Cppn<'a, A> {
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        for (i, node) in graph.nodes().iter().enumerate() {
            match node.node_type {
                CppnNodeType::Input => {
                    inputs.push(CppnNodeIndex::new(i));
                }
                CppnNodeType::Output => {
                    outputs.push(CppnNodeIndex::new(i));
                }
                _ => {}
            }
        }

        Cppn {
            graph: graph,
            inputs: inputs,
            outputs: outputs,
            incoming_signals: graph.nodes().iter().map(|_| 0.0).collect(),
        }
    }

    fn set_signal(&mut self, node_idx: CppnNodeIndex, value: f64) {
        self.incoming_signals[node_idx.index()] = value;
    }

    fn reset_signals(&mut self) {
        for value in self.incoming_signals.iter_mut() {
            *value = 0.0;
        }
    }

    /// Forward-propagate the signals starting from `from_nodes`. We use
    /// breadth-first-search (BFS).
    fn propagate_signals(&mut self, mut nodes: Vec<CppnNodeIndex>, mut seen: FixedBitSet) {
        while let Some(node_idx) = nodes.pop() {
            let node = &self.graph.nodes()[node_idx.index()];
            let input = self.incoming_signals[node_idx.index()];
            let output = match node.node_type {
                CppnNodeType::Hidden(ref activation_function) => {
                    // apply activation function on `input`
                    activation_function.calculate(input)
                }
                CppnNodeType::Input | CppnNodeType::Output => {
                    // Simply pass on the input signal.
                    input
                }
                CppnNodeType::Bias => CPPN_BIAS_WEIGHT,
            };

            // propagate output signal, to outgoing links.
            for out_link in &node.output_links {
                let out_node = out_link.node_idx.index();
                self.incoming_signals[out_node] += out_link.weight * output;
                if !seen.contains(out_node) {
                    seen.insert(out_node);
                    nodes.push(out_link.node_idx);
                }
            }
        }
    }

    /// Calculate all outputs
    pub fn calculate(&mut self, inputs: &[&[f64]]) -> Vec<f64> {
        assert!(self.incoming_signals.len() == self.graph.nodes().len());
        self.reset_signals();

        let mut nodes = Vec::new(); // XXX: worst case capacity
        let mut seen = FixedBitSet::with_capacity(self.incoming_signals.len());

        // assign all inputs
        let mut i = 0;
        for input_list in inputs.iter() {
            for &input in input_list.iter() {
                let input_idx = self.inputs[i];
                self.set_signal(input_idx, input);
                nodes.push(input_idx);
                seen.insert(input_idx.index());
                i += 1;
            }
        }
        assert!(i == self.inputs.len());

        // propagate the signals starting from the input signals.
        self.propagate_signals(nodes, seen);

        self.outputs
            .iter()
            .map(|&node_idx| self.incoming_signals[node_idx.index()])
            .collect()
    }
}

#[test]
fn test_cycle() {
    use super::bipolar::BipolarActivationFunction as AF;
    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    let h2 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    assert_eq!(true, g.valid_link(i1, i1).is_err());
    assert_eq!(true, g.valid_link(h1, h1).is_err());

    assert_eq!(true, g.valid_link(h1, i1).is_err());
    assert_eq!(Ok(()), g.valid_link(i1, h1));
    assert_eq!(Ok(()), g.valid_link(i1, h2));
    assert_eq!(Ok(()), g.valid_link(h1, h2));

    g.add_link(i1, h1, 0.0);
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(false, g.link_would_cycle(i1, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
    assert_eq!(true, g.link_would_cycle(i1, i1));
    assert_eq!(false, g.link_would_cycle(h1, h2));
    assert_eq!(false, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(h2, i1));

    g.add_link(h1, h2, 0.0);
    assert_eq!(true, g.link_would_cycle(h2, i1));
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(true, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
}

#[test]
fn test_simple_cppn() {
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Linear));
    let o1 = g.add_node(CppnNodeType::Output);
    g.add_link(i1, h1, 0.5);
    g.add_link(h1, o1, 1.0);

    let mut cppn = Cppn::new(&g);

    assert_eq!(vec![0.5 * 0.5], cppn.calculate(&[&[0.5]]));
    assert_eq!(vec![1.0], cppn.calculate(&[&[4.0]]));
    assert_eq!(vec![-1.0], cppn.calculate(&[&[-4.0]]));
}

#[test]
fn test_find_random_unconnected_link_no_cycle() {
    use rand;
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::<AF>::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let o1 = g.add_node(CppnNodeType::Output);
    let o2 = g.add_node(CppnNodeType::Output);

    let mut rng = rand::thread_rng();

    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    let l = link.unwrap();
    assert!((i1, o1) == l || (i1, o2) == l);

    g.add_link(i1, o2, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    assert_eq!((i1, o1), link.unwrap());

    g.add_link(i1, o1, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(false, link.is_some());
}
<commit_msg>output_links -> forward_links<commit_after>use activation_function::ActivationFunction;
use acyclic_network::{NodeType, Network, Link, Node};
pub use acyclic_network::NodeIndex as CppnNodeIndex;
use fixedbitset::FixedBitSet;

#[derive(Clone, Debug)]
pub enum CppnNodeType<A: ActivationFunction> {
    Bias,
    Input,
    Output,
    Hidden(A),
}

const CPPN_BIAS_WEIGHT: f64 = 1.0;

impl<A: ActivationFunction> NodeType for CppnNodeType<A> {
    fn accept_incoming_links(&self) -> bool {
        match *self {
            CppnNodeType::Input | CppnNodeType::Bias => false,
            _ => true,
        }
    }

    fn accept_outgoing_links(&self) -> bool {
        match *self {
            CppnNodeType::Output => false,
            _ => true,
        }
    }
}

pub type CppnLink = Link<f64>;
pub type CppnNode<A: ActivationFunction> = Node<CppnNodeType<A>, f64>;
pub type CppnGraph<A: ActivationFunction> = Network<CppnNodeType<A>, f64>;

/// Represents a Compositional Pattern Producing Network (CPPN)
pub struct Cppn<'a, A: ActivationFunction + 'a> {
    graph: &'a CppnGraph<A>,
    inputs: Vec<CppnNodeIndex>,
    outputs: Vec<CppnNodeIndex>,

    // For each node in `graph` there exists a corresponding field in `incoming_signals` describing
    // the sum of all input signals for that node.  We could store it inline in the `CppnNode`, but
    // this would require to make the whole CppnGraph mutable.
    incoming_signals: Vec<f64>,
}

impl<'a, A: ActivationFunction> Cppn<'a, A> {
    pub fn new(graph: &'a CppnGraph<A>) -> Cppn<'a, A> {
        let mut inputs = Vec::new();
        let mut outputs = Vec::new();

        for (i, node) in graph.nodes().iter().enumerate() {
            match node.node_type {
                CppnNodeType::Input => {
                    inputs.push(CppnNodeIndex::new(i));
                }
                CppnNodeType::Output => {
                    outputs.push(CppnNodeIndex::new(i));
                }
                _ => {}
            }
        }

        Cppn {
            graph: graph,
            inputs: inputs,
            outputs: outputs,
            incoming_signals: graph.nodes().iter().map(|_| 0.0).collect(),
        }
    }

    fn set_signal(&mut self, node_idx: CppnNodeIndex, value: f64) {
        self.incoming_signals[node_idx.index()] = value;
    }

    fn reset_signals(&mut self) {
        for value in self.incoming_signals.iter_mut() {
            *value = 0.0;
        }
    }

    /// Forward-propagate the signals starting from `from_nodes`. We use
    /// breadth-first-search (BFS).
    fn propagate_signals(&mut self, mut nodes: Vec<CppnNodeIndex>, mut seen: FixedBitSet) {
        while let Some(node_idx) = nodes.pop() {
            let node = &self.graph.nodes()[node_idx.index()];
            let input = self.incoming_signals[node_idx.index()];
            let output = match node.node_type {
                CppnNodeType::Hidden(ref activation_function) => {
                    // apply activation function on `input`
                    activation_function.calculate(input)
                }
                CppnNodeType::Input | CppnNodeType::Output => {
                    // Simply pass on the input signal.
                    input
                }
                CppnNodeType::Bias => CPPN_BIAS_WEIGHT,
            };

            // propagate output signal, to outgoing links.
            for out_link in &node.forward_links {
                let out_node = out_link.node_idx.index();
                self.incoming_signals[out_node] += out_link.weight * output;
                if !seen.contains(out_node) {
                    seen.insert(out_node);
                    nodes.push(out_link.node_idx);
                }
            }
        }
    }

    /// Calculate all outputs
    pub fn calculate(&mut self, inputs: &[&[f64]]) -> Vec<f64> {
        assert!(self.incoming_signals.len() == self.graph.nodes().len());
        self.reset_signals();

        let mut nodes = Vec::new(); // XXX: worst case capacity
        let mut seen = FixedBitSet::with_capacity(self.incoming_signals.len());

        // assign all inputs
        let mut i = 0;
        for input_list in inputs.iter() {
            for &input in input_list.iter() {
                let input_idx = self.inputs[i];
                self.set_signal(input_idx, input);
                nodes.push(input_idx);
                seen.insert(input_idx.index());
                i += 1;
            }
        }
        assert!(i == self.inputs.len());

        // propagate the signals starting from the input signals.
        self.propagate_signals(nodes, seen);

        self.outputs
            .iter()
            .map(|&node_idx| self.incoming_signals[node_idx.index()])
            .collect()
    }
}

#[test]
fn test_cycle() {
    use super::bipolar::BipolarActivationFunction as AF;
    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    let h2 = g.add_node(CppnNodeType::Hidden(AF::Identity));
    assert_eq!(true, g.valid_link(i1, i1).is_err());
    assert_eq!(true, g.valid_link(h1, h1).is_err());

    assert_eq!(true, g.valid_link(h1, i1).is_err());
    assert_eq!(Ok(()), g.valid_link(i1, h1));
    assert_eq!(Ok(()), g.valid_link(i1, h2));
    assert_eq!(Ok(()), g.valid_link(h1, h2));

    g.add_link(i1, h1, 0.0);
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(false, g.link_would_cycle(i1, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
    assert_eq!(true, g.link_would_cycle(i1, i1));
    assert_eq!(false, g.link_would_cycle(h1, h2));
    assert_eq!(false, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(h2, i1));

    g.add_link(h1, h2, 0.0);
    assert_eq!(true, g.link_would_cycle(h2, i1));
    assert_eq!(true, g.link_would_cycle(h1, i1));
    assert_eq!(true, g.link_would_cycle(h2, h1));
    assert_eq!(false, g.link_would_cycle(i1, h2));
}

#[test]
fn test_simple_cppn() {
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let h1 = g.add_node(CppnNodeType::Hidden(AF::Linear));
    let o1 = g.add_node(CppnNodeType::Output);
    g.add_link(i1, h1, 0.5);
    g.add_link(h1, o1, 1.0);

    let mut cppn = Cppn::new(&g);

    assert_eq!(vec![0.5 * 0.5], cppn.calculate(&[&[0.5]]));
    assert_eq!(vec![1.0], cppn.calculate(&[&[4.0]]));
    assert_eq!(vec![-1.0], cppn.calculate(&[&[-4.0]]));
}

#[test]
fn test_find_random_unconnected_link_no_cycle() {
    use rand;
    use super::bipolar::BipolarActivationFunction as AF;

    let mut g = CppnGraph::<AF>::new();
    let i1 = g.add_node(CppnNodeType::Input);
    let o1 = g.add_node(CppnNodeType::Output);
    let o2 = g.add_node(CppnNodeType::Output);

    let mut rng = rand::thread_rng();

    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    let l = link.unwrap();
    assert!((i1, o1) == l || (i1, o2) == l);

    g.add_link(i1, o2, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(true, link.is_some());
    assert_eq!((i1, o1), link.unwrap());

    g.add_link(i1, o1, 0.0);
    let link = g.find_random_unconnected_link_no_cycle(&mut rng);
    assert_eq!(false, link.is_some());
}
<|endoftext|>