<commit_before>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

#[feature(globs)];
#[feature(macro_rules)];

#[link(name = "benchmark", vers = "alpha", author = "dwrensha")];

#[crate_type = "bin"];

extern mod capnprust;

pub mod common;

pub mod carsales_capnp;
pub mod carsales;

pub mod catrank_capnp;
pub mod catrank;

pub mod eval_capnp;
pub mod eval;

mod Uncompressed {
    use capnprust;
    use std;

    pub fn write<T : std::rt::io::Writer>(writer: &mut T,
                                          message: &capnprust::message::MessageBuilder) {
        capnprust::serialize::writeMessage(writer, message);
    }

    pub fn newReader<U : std::rt::io::Reader, T>(
        inputStream : &mut U,
        options : capnprust::message::ReaderOptions,
        cont : &fn(v : &mut capnprust::message::MessageReader) -> T) -> T {
        capnprust::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
}

mod Packed {
    use capnprust;
    use std;

    pub fn write<T : std::rt::io::Writer>(writer: &mut T,
                                          message: &capnprust::message::MessageBuilder) {
        capnprust::serialize_packed::writePackedMessage(writer, message);
    }

    pub fn newReader<U : std::rt::io::Reader, T>(
        inputStream : &mut U,
        options : capnprust::message::ReaderOptions,
        cont : &fn(v : &mut capnprust::message::MessageReader) -> T) -> T {
        capnprust::serialize::InputStreamMessageReader::new(
            &mut capnprust::serialize_packed::PackedInputStream{inner : inputStream},
            options, cont)
    }
}

macro_rules! passByObject(
    ( $testcase:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnprust::message::MessageBuilder::new_default();
                let mut messageRes = capnprust::message::MessageBuilder::new_default();

                let request = messageReq.initRoot::<$testcase::RequestBuilder>();
                let response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                let expected = $testcase::setupRequest(&mut rng, request);

                do request.asReader |requestReader| {
                    $testcase::handleRequest(requestReader, response);
                }

                do response.asReader |responseReader| {
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        println("Incorrect response.");
                    }
                }
            }
        });
    )

macro_rules! passByBytes(
    ( $testcase:ident, $compression:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnprust::message::MessageBuilder::new_default();
                let mut messageRes = capnprust::message::MessageBuilder::new_default();

                let request = messageReq.initRoot::<$testcase::RequestBuilder>();
                let response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                let expected = $testcase::setupRequest(&mut rng, request);

                let requestBytes = do std::rt::io::mem::with_mem_writer |writer| {
                    $compression::write(writer, messageReq)
                };

                do $compression::newReader(
                      &mut std::rt::io::mem::BufReader::new(requestBytes),
                      capnprust::message::DEFAULT_READER_OPTIONS) |requestReader| {
                    let requestReader = $testcase::newRequestReader(requestReader.getRoot());
                    $testcase::handleRequest(requestReader, response);
                }

                let responseBytes = do std::rt::io::mem::with_mem_writer |writer| {
                    $compression::write(writer, messageRes);
                };

                do $compression::newReader(
                    &mut std::rt::io::mem::BufReader::new(responseBytes),
                    capnprust::message::DEFAULT_READER_OPTIONS) |responseReader| {
                    let responseReader = $testcase::newResponseReader(responseReader.getRoot());
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        println("Incorrect response.");
                    }
                }
            }
        });
    )

macro_rules! server(
    ( $testcase:ident, $iters:expr) => ({
            fail!("unimplemented")
        });
    )

macro_rules! syncClient(
    ( $testcase:ident, $iters:expr) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnprust::message::MessageBuilder::new_default();
                let mut messageRes = capnprust::message::MessageBuilder::new_default();

                let request = messageReq.initRoot::<$testcase::RequestBuilder>();
                let _response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                let _expected = $testcase::setupRequest(&mut rng, request);
                fail!("unimplemented");
            }
        });
    )

macro_rules! passByPipe(
    ( $testcase:ident, $compression:ident, $iters:expr) => ({
            use std::rt::io::process;

            // get a rustc crash if we put this in line below
            let io = ~[process::CreatePipe(true, false), // stdin
                       process::CreatePipe(false, true), // stdout
                       process::Ignored];

            let config = process::ProcessConfig {
                program: "./benchmark/benchmark",
                args: [],
                env : None,
                cwd: None,
                io : io
            };
            match process::Process::new(config) {
                Some(ref mut p) => {
                    println!("{:?}", p);
                    let s = p.io[1].read_to_end();
                    std::rt::io::stdout().write(s);
                    println!("{}", p.wait());
                }
                None => {
                    println("bummer");
                }
            }
        });
    )

macro_rules! doTestcase(
    ( $testcase:ident, $mode:expr, $reuse:expr, $compression:ident, $iters:expr ) => ({
            match $mode {
                ~"object" => passByObject!($testcase, $iters),
                ~"bytes" => passByBytes!($testcase, $compression, $iters),
                ~"server" => server!($testcase, $iters),
                ~"client" => syncClient!($testcase, $iters),
                ~"pipe" => passByPipe!($testcase, $compression, $iters),
                s => fail!("unrecognized mode: {}", s)
            }
        });
    )

macro_rules! doTestcase1(
    ( $testcase:expr, $mode:expr, $reuse:expr, $compression:ident, $iters:expr) => ({
            match $testcase {
                ~"carsales" => doTestcase!(carsales, $mode, $reuse, $compression, $iters),
                ~"catrank" => doTestcase!(catrank, $mode, $reuse, $compression, $iters),
                ~"eval" => doTestcase!(eval, $mode, $reuse, $compression, $iters),
                s => fail!("unrecognized test case: {}", s)
            }
        });
    )

pub fn main () {

    let args = std::os::args();

    if (args.len() != 6) {
        println!("USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT", args[0]);
        std::os::set_exit_status(1);
        return;
    }

    let iters = match from_str::<u64>(args[5]) {
        Some (n) => n,
        None => {
            println!("Could not parse a u64 from: {}", args[5]);
            std::os::set_exit_status(1);
            return;
        }
    };

    match args[4] {
        ~"none" => doTestcase1!(args[1], args[2],  args[3], Uncompressed, iters),
        ~"packed" => doTestcase1!(args[1], args[2], args[3], Packed, iters),
        s => fail!("unrecognized compression: {}", s)
    }

}
<commit_msg>argument manip<commit_after>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

#[feature(globs)];
#[feature(macro_rules)];

#[link(name = "benchmark", vers = "alpha", author = "dwrensha")];

#[crate_type = "bin"];

extern mod capnprust;

pub mod common;

pub mod carsales_capnp;
pub mod carsales;

pub mod catrank_capnp;
pub mod catrank;

pub mod eval_capnp;
pub mod eval;

mod Uncompressed {
    use capnprust;
    use std;

    pub fn write<T : std::rt::io::Writer>(writer: &mut T,
                                          message: &capnprust::message::MessageBuilder) {
        capnprust::serialize::writeMessage(writer, message);
    }

    pub fn newReader<U : std::rt::io::Reader, T>(
        inputStream : &mut U,
        options : capnprust::message::ReaderOptions,
        cont : &fn(v : &mut capnprust::message::MessageReader) -> T) -> T {
        capnprust::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
}

mod Packed {
    use capnprust;
    use std;

    pub fn write<T : std::rt::io::Writer>(writer: &mut T,
                                          message: &capnprust::message::MessageBuilder) {
        capnprust::serialize_packed::writePackedMessage(writer, message);
    }

    pub fn newReader<U : std::rt::io::Reader, T>(
        inputStream : &mut U,
        options : capnprust::message::ReaderOptions,
        cont : &fn(v : &mut capnprust::message::MessageReader) -> T) -> T {
        capnprust::serialize::InputStreamMessageReader::new(
            &mut capnprust::serialize_packed::PackedInputStream{inner : inputStream},
            options, cont)
    }
}

macro_rules! passByObject(
    ( $testcase:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnprust::message::MessageBuilder::new_default();
                let mut messageRes = capnprust::message::MessageBuilder::new_default();

                let request = messageReq.initRoot::<$testcase::RequestBuilder>();
                let response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                let expected = $testcase::setupRequest(&mut rng, request);

                do request.asReader |requestReader| {
                    $testcase::handleRequest(requestReader, response);
                }

                do response.asReader |responseReader| {
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        println("Incorrect response.");
                    }
                }
            }
        });
    )

macro_rules! passByBytes(
    ( $testcase:ident, $compression:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnprust::message::MessageBuilder::new_default();
                let mut messageRes = capnprust::message::MessageBuilder::new_default();

                let request = messageReq.initRoot::<$testcase::RequestBuilder>();
                let response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                let expected = $testcase::setupRequest(&mut rng, request);

                let requestBytes = do std::rt::io::mem::with_mem_writer |writer| {
                    $compression::write(writer, messageReq)
                };

                do $compression::newReader(
                      &mut std::rt::io::mem::BufReader::new(requestBytes),
                      capnprust::message::DEFAULT_READER_OPTIONS) |requestReader| {
                    let requestReader = $testcase::newRequestReader(requestReader.getRoot());
                    $testcase::handleRequest(requestReader, response);
                }

                let responseBytes = do std::rt::io::mem::with_mem_writer |writer| {
                    $compression::write(writer, messageRes);
                };

                do $compression::newReader(
                    &mut std::rt::io::mem::BufReader::new(responseBytes),
                    capnprust::message::DEFAULT_READER_OPTIONS) |responseReader| {
                    let responseReader = $testcase::newResponseReader(responseReader.getRoot());
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        println("Incorrect response.");
                    }
                }
            }
        });
    )

macro_rules! server(
    ( $testcase:ident, $compression:ident, $iters:expr, $input:expr, $output:expr) => ({

        });
    )

macro_rules! syncClient(
    ( $testcase:ident, $iters:expr) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnprust::message::MessageBuilder::new_default();
                let mut messageRes = capnprust::message::MessageBuilder::new_default();

                let request = messageReq.initRoot::<$testcase::RequestBuilder>();
                let _response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                let _expected = $testcase::setupRequest(&mut rng, request);
                fail!("unimplemented");
            }
        });
    )

macro_rules! passByPipe(
    ( $testcase:ident, $compression:ident, $iters:expr) => ({
            use std::rt::io::process;

            // get a rustc crash if we put this in line below
            let io = ~[process::CreatePipe(true, false), // stdin
                       process::CreatePipe(false, true), // stdout
                       process::Ignored];

            let mut args = std::os::args();
            args[2] = ~"client";

            let config = process::ProcessConfig {
                program: "./benchmark/benchmark",
                args: args.slice(1, args.len()),
                env : None,
                cwd: None,
                io : io
            };
            match process::Process::new(config) {
                Some(ref mut p) => {
                    server!($testcase, $compression, $iters, p.io[1], p.io[0]);
                    println!("{}", p.wait());
                }
                None => {
                    println("bummer");
                }
            }
        });
    )

macro_rules! doTestcase(
    ( $testcase:ident, $mode:expr, $reuse:expr, $compression:ident, $iters:expr ) => ({
            match $mode {
                ~"object" => passByObject!($testcase, $iters),
                ~"bytes" => passByBytes!($testcase, $compression, $iters),
                ~"client" => syncClient!($testcase, $iters),
                ~"pipe" => passByPipe!($testcase, $compression, $iters),
                s => fail!("unrecognized mode: {}", s)
            }
        });
    )

macro_rules! doTestcase1(
    ( $testcase:expr, $mode:expr, $reuse:expr, $compression:ident, $iters:expr) => ({
            match $testcase {
                ~"carsales" => doTestcase!(carsales, $mode, $reuse, $compression, $iters),
                ~"catrank" => doTestcase!(catrank, $mode, $reuse, $compression, $iters),
                ~"eval" => doTestcase!(eval, $mode, $reuse, $compression, $iters),
                s => fail!("unrecognized test case: {}", s)
            }
        });
    )

pub fn main () {

    let args = std::os::args();

    if (args.len() != 6) {
        println!("USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT", args[0]);
        std::os::set_exit_status(1);
        return;
    }

    let iters = match from_str::<u64>(args[5]) {
        Some (n) => n,
        None => {
            println!("Could not parse a u64 from: {}", args[5]);
            std::os::set_exit_status(1);
            return;
        }
    };

    match args[4] {
        ~"none" => doTestcase1!(args[1], args[2],  args[3], Uncompressed, iters),
        ~"packed" => doTestcase1!(args[1], args[2], args[3], Packed, iters),
        s => fail!("unrecognized compression: {}", s)
    }

}
<|endoftext|>


<commit_before>extern crate clap;
extern crate clippy_dev;
extern crate regex;

use clap::{App, Arg, SubCommand};
use clippy_dev::*;
mod stderr_length_check;

#[derive(PartialEq)]
enum UpdateMode {
    Check,
    Change,
}

fn main() {
    let matches = App::new("Clippy developer tooling")
        .subcommand(
            SubCommand::with_name("update_lints")
                .about("Updates lint registration and information from the source code")
                .long_about(
                    "Makes sure that:\n \
                     * the lint count in README.md is correct\n \
                     * the changelog contains markdown link references at the bottom\n \
                     * all lint groups include the correct lints\n \
                     * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
                     * all lints are registered in the lint store",
                )
                .arg(Arg::with_name("print-only").long("print-only").help(
                    "Print a table of lints to STDOUT. \
                     This does not include deprecated and internal lints. \
                     (Does not modify any files)",
                ))
                .arg(
                    Arg::with_name("check")
                        .long("check")
                        .help("Checks that util/dev update_lints has been run. Used on CI."),
                ),
        )
        .arg(
            Arg::with_name("limit-stderr-length")
                .long("limit-stderr-length")
                .help("Ensures that stderr files do not grow longer than a certain amount of lines."),
        )
        .get_matches();

    if matches.is_present("limit-stderr-length") {
        stderr_length_check::check();
    }
    if let Some(matches) = matches.subcommand_matches("update_lints") {
        if matches.is_present("print-only") {
            print_lints();
        } else if matches.is_present("check") {
            update_lints(&UpdateMode::Check);
        } else {
            update_lints(&UpdateMode::Change);
        }
    }
}

fn print_lints() {
    let lint_list = gather_all();
    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
    let lint_count = usable_lints.len();
    let grouped_by_lint_group = Lint::by_lint_group(&usable_lints);

    for (lint_group, mut lints) in grouped_by_lint_group {
        if lint_group == "Deprecated" {
            continue;
        }
        println!("\n## {}", lint_group);

        lints.sort_by_key(|l| l.name.clone());

        for lint in lints {
            println!(
                "* [{}]({}#{}) ({})",
                lint.name,
                clippy_dev::DOCS_LINK.clone(),
                lint.name,
                lint.desc
            );
        }
    }

    println!("there are {} lints", lint_count);
}

fn update_lints(update_mode: &UpdateMode) {
    let lint_list: Vec<Lint> = gather_all().collect();

    std::fs::write(
        "../src/lintlist.rs",
        &format!(
            "\
/// Lint data parsed from the Clippy source code.
#[derive(Clone, PartialEq, Debug)]
pub struct Lint {{
    pub name: &'static str,
    pub group: &'static str,
    pub desc: &'static str,
    pub deprecation: Option<&'static str>,
    pub module: &'static str,
}}

pub const ALL_LINTS: [Lint; {}] = {:#?};",
            lint_list.len(),
            lint_list
        ),
    )
    .expect("can write to file");

    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
    let lint_count = usable_lints.len();

    let mut file_change = replace_region_in_file(
        "../README.md",
        r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang.github.io/rust-clippy/master/index.html\)"#,
        "",
        true,
        update_mode == &UpdateMode::Change,
        || {
            vec![
                format!("[There are {} lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)", lint_count)
            ]
        }
    ).changed;

    file_change |= replace_region_in_file(
        "../CHANGELOG.md",
        "<!-- begin autogenerated links to lint list -->",
        "<!-- end autogenerated links to lint list -->",
        false,
        update_mode == &UpdateMode::Change,
        || gen_changelog_lint_list(lint_list.clone()),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin deprecated lints",
        "end deprecated lints",
        false,
        update_mode == &UpdateMode::Change,
        || gen_deprecated(&lint_list),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin lints modules",
        "end lints modules",
        false,
        update_mode == &UpdateMode::Change,
        || gen_modules_list(lint_list.clone()),
    )
    .changed;

    // Generate lists of lints in the clippy::all lint group
    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        r#"reg.register_lint_group\("clippy::all""#,
        r#"\]\);"#,
        false,
        update_mode == &UpdateMode::Change,
        || {
            // clippy::all should only include the following lint groups:
            let all_group_lints = usable_lints
                .clone()
                .into_iter()
                .filter(|l| {
                    l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
                })
                .collect();

            gen_lint_group_list(all_group_lints)
        },
    )
    .changed;

    // Generate the list of lints for all other lint groups
    for (lint_group, lints) in Lint::by_lint_group(&usable_lints) {
        file_change |= replace_region_in_file(
            "../clippy_lints/src/lib.rs",
            &format!("reg.register_lint_group\\(\"clippy::{}\"", lint_group),
            r#"\]\);"#,
            false,
            update_mode == &UpdateMode::Change,
            || gen_lint_group_list(lints.clone()),
        )
        .changed;
    }

    if update_mode == &UpdateMode::Check && file_change {
        println!(
            "Not all lints defined properly. \
             Please run `util/dev update_lints` to make sure all lints are defined properly."
        );
        std::process::exit(1);
    }
}
<commit_msg>Update clippy_dev/src/main.rs<commit_after>extern crate clap;
extern crate clippy_dev;
extern crate regex;

use clap::{App, Arg, SubCommand};
use clippy_dev::*;
mod stderr_length_check;

#[derive(PartialEq)]
enum UpdateMode {
    Check,
    Change,
}

fn main() {
    let matches = App::new("Clippy developer tooling")
        .subcommand(
            SubCommand::with_name("update_lints")
                .about("Updates lint registration and information from the source code")
                .long_about(
                    "Makes sure that:\n \
                     * the lint count in README.md is correct\n \
                     * the changelog contains markdown link references at the bottom\n \
                     * all lint groups include the correct lints\n \
                     * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
                     * all lints are registered in the lint store",
                )
                .arg(Arg::with_name("print-only").long("print-only").help(
                    "Print a table of lints to STDOUT. \
                     This does not include deprecated and internal lints. \
                     (Does not modify any files)",
                ))
                .arg(
                    Arg::with_name("check")
                        .long("check")
                        .help("Checks that util/dev update_lints has been run. Used on CI."),
                ),
        )
        .arg(
            Arg::with_name("limit-stderr-length")
                .long("limit-stderr-length")
                .help("Ensures that stderr files do not grow longer than a certain amount of lines."),
        )
        .get_matches();

    if matches.is_present("limit-stderr-length") {
        stderr_length_check::check();
    }
    if let Some(matches) = matches.subcommand_matches("update_lints") {
        if matches.is_present("print-only") {
            print_lints();
        } else if matches.is_present("check") {
            update_lints(&UpdateMode::Check);
        } else {
            update_lints(&UpdateMode::Change);
        }
    }
}

fn print_lints() {
    let lint_list = gather_all();
    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
    let lint_count = usable_lints.len();
    let grouped_by_lint_group = Lint::by_lint_group(&usable_lints);

    for (lint_group, mut lints) in grouped_by_lint_group {
        if lint_group == "Deprecated" {
            continue;
        }
        println!("\n## {}", lint_group);

        lints.sort_by_key(|l| l.name.clone());

        for lint in lints {
            println!(
                "* [{}]({}#{}) ({})",
                lint.name,
                clippy_dev::DOCS_LINK.clone(),
                lint.name,
                lint.desc
            );
        }
    }

    println!("there are {} lints", lint_count);
}

fn update_lints(update_mode: &UpdateMode) {
    let lint_list: Vec<Lint> = gather_all().collect();

    std::fs::write(
        "../src/lintlist.rs",
        &format!(
            "\
/// Lint data parsed from the Clippy source code.
#[derive(Clone, PartialEq, Debug)]
pub struct Lint {{
    pub name: &'static str,
    pub group: &'static str,
    pub desc: &'static str,
    pub deprecation: Option<&'static str>,
    pub module: &'static str,
}}

pub const ALL_LINTS: [Lint; {}] = {:#?};\n",
            lint_list.len(),
            lint_list
        ),
    )
    .expect("can write to file");

    let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
    let lint_count = usable_lints.len();

    let mut file_change = replace_region_in_file(
        "../README.md",
        r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang.github.io/rust-clippy/master/index.html\)"#,
        "",
        true,
        update_mode == &UpdateMode::Change,
        || {
            vec![
                format!("[There are {} lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)", lint_count)
            ]
        }
    ).changed;

    file_change |= replace_region_in_file(
        "../CHANGELOG.md",
        "<!-- begin autogenerated links to lint list -->",
        "<!-- end autogenerated links to lint list -->",
        false,
        update_mode == &UpdateMode::Change,
        || gen_changelog_lint_list(lint_list.clone()),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin deprecated lints",
        "end deprecated lints",
        false,
        update_mode == &UpdateMode::Change,
        || gen_deprecated(&lint_list),
    )
    .changed;

    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        "begin lints modules",
        "end lints modules",
        false,
        update_mode == &UpdateMode::Change,
        || gen_modules_list(lint_list.clone()),
    )
    .changed;

    // Generate lists of lints in the clippy::all lint group
    file_change |= replace_region_in_file(
        "../clippy_lints/src/lib.rs",
        r#"reg.register_lint_group\("clippy::all""#,
        r#"\]\);"#,
        false,
        update_mode == &UpdateMode::Change,
        || {
            // clippy::all should only include the following lint groups:
            let all_group_lints = usable_lints
                .clone()
                .into_iter()
                .filter(|l| {
                    l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
                })
                .collect();

            gen_lint_group_list(all_group_lints)
        },
    )
    .changed;

    // Generate the list of lints for all other lint groups
    for (lint_group, lints) in Lint::by_lint_group(&usable_lints) {
        file_change |= replace_region_in_file(
            "../clippy_lints/src/lib.rs",
            &format!("reg.register_lint_group\\(\"clippy::{}\"", lint_group),
            r#"\]\);"#,
            false,
            update_mode == &UpdateMode::Change,
            || gen_lint_group_list(lints.clone()),
        )
        .changed;
    }

    if update_mode == &UpdateMode::Check && file_change {
        println!(
            "Not all lints defined properly. \
             Please run `util/dev update_lints` to make sure all lints are defined properly."
        );
        std::process::exit(1);
    }
}
<|endoftext|>


<commit_before>use libc;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::ptr;
use std::rc::Rc;

use allowed_url::AllowedUrl;
use handle::{self, RsvgHandle};
use node::{Node, RsvgNode};
use util::utf8_cstr;

pub enum RsvgDefs {}

pub struct Defs {
    nodes: HashMap<String, Rc<Node>>,
    externs: HashMap<String, *const RsvgHandle>,
}

impl Defs {
    pub fn new() -> Defs {
        Defs {
            nodes: Default::default(),
            externs: Default::default(),
        }
    }

    pub fn insert(&mut self, id: &str, node: &Rc<Node>) {
        self.nodes.entry(id.to_string()).or_insert(node.clone());
    }

    /// Returns a node from an URI reference, or `None`
    ///
    /// This may return a node within the same RSVG handle, or a node in a secondary RSVG
    /// handle that is referenced by the current one.  If the element's id is not found,
    /// returns `None`.
    pub fn lookup(&mut self, handle: *const RsvgHandle, name: &str) -> Option<&Rc<Node>> {
        if let Ok(reference) = Reference::parse(name) {
            match reference {
                Reference::PlainUri(_) => None,
                Reference::FragmentId(fragment) => self.nodes.get(&fragment),
                Reference::UriWithFragmentId(href, fragment) => {
                    match self.get_extern_handle(handle, &href) {
                        Ok(extern_handle) => handle::get_defs(extern_handle).nodes.get(&fragment),
                        Err(()) => None,
                    }
                }
            }
        } else {
            None
        }
    }

    fn get_extern_handle(
        &mut self,
        handle: *const RsvgHandle,
        href: &str,
    ) -> Result<*const RsvgHandle, ()> {
        let aurl =
            AllowedUrl::from_href(href, handle::get_base_url(handle).as_ref()).map_err(|_| ())?;

        match self.externs.entry(aurl.url().as_str().to_string()) {
            Entry::Occupied(e) => Ok(*(e.get())),
            Entry::Vacant(e) => {
                let extern_handle = handle::load_extern(handle, e.key())?;
                e.insert(extern_handle);
                Ok(extern_handle)
            }
        }
    }
}

/// Represents a possibly non-canonical URI with an optional fragment identifier
///
/// Sometimes in SVG element references (e.g. the `href` in the `<feImage>` element) we
/// must decide between referencing an external file, or using a plain fragment identifier
/// like `href="#foo"` as a reference to an SVG element in the same file as the one being
/// processes.  This enum makes that distinction.
#[derive(Debug, PartialEq)]
pub enum Reference {
    PlainUri(String),
    FragmentId(String),
    UriWithFragmentId(String, String),
}

impl Reference {
    pub fn parse(s: &str) -> Result<Reference, ()> {
        let (uri, fragment) = match s.rfind('#') {
            None => (Some(s), None),
            Some(p) if p == 0 => (None, Some(&s[1..])),
            Some(p) => (Some(&s[..p]), Some(&s[(p + 1)..])),
        };

        match (uri, fragment) {
            (None, Some(f)) if f.len() == 0 => Err(()),
            (None, Some(f)) => Ok(Reference::FragmentId(f.to_string())),
            (Some(u), _) if u.len() == 0 => Err(()),
            (Some(u), None) => Ok(Reference::PlainUri(u.to_string())),
            (Some(_u), Some(f)) if f.len() == 0 => Err(()),
            (Some(u), Some(f)) => Ok(Reference::UriWithFragmentId(u.to_string(), f.to_string())),
            (_, _) => Err(()),
        }
    }
}

#[no_mangle]
pub extern "C" fn rsvg_defs_free(defs: *mut RsvgDefs) {
    assert!(!defs.is_null());

    unsafe {
        let defs = { &mut *(defs as *mut Defs) };
        Box::from_raw(defs);
    }
}

#[no_mangle]
pub extern "C" fn rsvg_defs_lookup(
    defs: *mut RsvgDefs,
    handle: *const RsvgHandle,
    name: *const libc::c_char,
) -> *const RsvgNode {
    assert!(!defs.is_null());
    assert!(!name.is_null());

    let defs = unsafe { &mut *(defs as *mut Defs) };
    let name = unsafe { utf8_cstr(name) };

    match defs.lookup(handle, name) {
        Some(n) => n as *const RsvgNode,
        None => ptr::null(),
    }
}

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

    #[test]
    fn reference_kinds() {
        assert_eq!(Reference::parse("uri"), Ok(Reference::PlainUri("uri".to_string())));
        assert_eq!(
            Reference::parse("#fragment"),
            Ok(Reference::FragmentId("fragment".to_string()))
        );
        assert_eq!(
            Reference::parse("uri#fragment"),
            Ok(Reference::UriWithFragmentId("uri".to_string(), "fragment".to_string()))
        );
    }

    #[test]
    fn reference_errors() {
        assert!(Reference::parse("").is_err());
        assert!(Reference::parse("#").is_err());
        assert!(Reference::parse("uri#").is_err());
    }
}
<commit_msg>Reference::parse(): Return a ReferenceError, not a ()<commit_after>use libc;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::ptr;
use std::rc::Rc;

use allowed_url::AllowedUrl;
use handle::{self, RsvgHandle};
use node::{Node, RsvgNode};
use util::utf8_cstr;

pub enum RsvgDefs {}

pub struct Defs {
    nodes: HashMap<String, Rc<Node>>,
    externs: HashMap<String, *const RsvgHandle>,
}

impl Defs {
    pub fn new() -> Defs {
        Defs {
            nodes: Default::default(),
            externs: Default::default(),
        }
    }

    pub fn insert(&mut self, id: &str, node: &Rc<Node>) {
        self.nodes.entry(id.to_string()).or_insert(node.clone());
    }

    /// Returns a node from an URI reference, or `None`
    ///
    /// This may return a node within the same RSVG handle, or a node in a secondary RSVG
    /// handle that is referenced by the current one.  If the element's id is not found,
    /// returns `None`.
    pub fn lookup(&mut self, handle: *const RsvgHandle, name: &str) -> Option<&Rc<Node>> {
        if let Ok(reference) = Reference::parse(name) {
            match reference {
                Reference::PlainUri(_) => None,
                Reference::FragmentId(fragment) => self.nodes.get(&fragment),
                Reference::UriWithFragmentId(href, fragment) => {
                    match self.get_extern_handle(handle, &href) {
                        Ok(extern_handle) => handle::get_defs(extern_handle).nodes.get(&fragment),
                        Err(()) => None,
                    }
                }
            }
        } else {
            None
        }
    }

    fn get_extern_handle(
        &mut self,
        handle: *const RsvgHandle,
        href: &str,
    ) -> Result<*const RsvgHandle, ()> {
        let aurl =
            AllowedUrl::from_href(href, handle::get_base_url(handle).as_ref()).map_err(|_| ())?;

        match self.externs.entry(aurl.url().as_str().to_string()) {
            Entry::Occupied(e) => Ok(*(e.get())),
            Entry::Vacant(e) => {
                let extern_handle = handle::load_extern(handle, e.key())?;
                e.insert(extern_handle);
                Ok(extern_handle)
            }
        }
    }
}

/// Represents a possibly non-canonical URI with an optional fragment identifier
///
/// Sometimes in SVG element references (e.g. the `href` in the `<feImage>` element) we
/// must decide between referencing an external file, or using a plain fragment identifier
/// like `href="#foo"` as a reference to an SVG element in the same file as the one being
/// processes.  This enum makes that distinction.
#[derive(Debug, PartialEq)]
pub enum Reference {
    PlainUri(String),
    FragmentId(String),
    UriWithFragmentId(String, String),
}

#[derive(Debug, PartialEq)]
pub enum ReferenceError {
    ParseError,
}

impl Reference {
    pub fn parse(s: &str) -> Result<Reference, ReferenceError> {
        let (uri, fragment) = match s.rfind('#') {
            None => (Some(s), None),
            Some(p) if p == 0 => (None, Some(&s[1..])),
            Some(p) => (Some(&s[..p]), Some(&s[(p + 1)..])),
        };

        match (uri, fragment) {
            (None, Some(f)) if f.len() == 0 => Err(ReferenceError::ParseError),
            (None, Some(f)) => Ok(Reference::FragmentId(f.to_string())),
            (Some(u), _) if u.len() == 0 => Err(ReferenceError::ParseError),
            (Some(u), None) => Ok(Reference::PlainUri(u.to_string())),
            (Some(_u), Some(f)) if f.len() == 0 => Err(ReferenceError::ParseError),
            (Some(u), Some(f)) => Ok(Reference::UriWithFragmentId(u.to_string(), f.to_string())),
            (_, _) => Err(ReferenceError::ParseError),
        }
    }
}

#[no_mangle]
pub extern "C" fn rsvg_defs_free(defs: *mut RsvgDefs) {
    assert!(!defs.is_null());

    unsafe {
        let defs = { &mut *(defs as *mut Defs) };
        Box::from_raw(defs);
    }
}

#[no_mangle]
pub extern "C" fn rsvg_defs_lookup(
    defs: *mut RsvgDefs,
    handle: *const RsvgHandle,
    name: *const libc::c_char,
) -> *const RsvgNode {
    assert!(!defs.is_null());
    assert!(!name.is_null());

    let defs = unsafe { &mut *(defs as *mut Defs) };
    let name = unsafe { utf8_cstr(name) };

    match defs.lookup(handle, name) {
        Some(n) => n as *const RsvgNode,
        None => ptr::null(),
    }
}

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

    #[test]
    fn reference_kinds() {
        assert_eq!(Reference::parse("uri"), Ok(Reference::PlainUri("uri".to_string())));
        assert_eq!(
            Reference::parse("#fragment"),
            Ok(Reference::FragmentId("fragment".to_string()))
        );
        assert_eq!(
            Reference::parse("uri#fragment"),
            Ok(Reference::UriWithFragmentId("uri".to_string(), "fragment".to_string()))
        );
    }

    #[test]
    fn reference_errors() {
        assert_eq!(Reference::parse(""), Err(ReferenceError::ParseError));
        assert_eq!(Reference::parse("#"), Err(ReferenceError::ParseError));
        assert_eq!(Reference::parse("uri#"), Err(ReferenceError::ParseError));
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::mpsc;
use websocket::{Server, Message, Sender, Receiver};
use websocket::server::sender;
use websocket::stream::WebSocketStream;
use std::collections::{HashMap, BitSet};
use std::io::prelude::*;
use std::num::ToPrimitive;

use value::Value;
use index;
use flow::{Changes, FlowState, Flow};
use compiler::{compile, World};
use rustc_serialize::json::{Json, ToJson};

trait FromJson {
    fn from_json(json: &Json) -> Self;
}

impl ToJson for Value {
    fn to_json(&self) -> Json {
        match *self {
            Value::String(ref string) => Json::String(string.clone()),
            Value::Float(float) => Json::F64(float),
            Value::Tuple(ref tuple) => tuple.to_json(),
            Value::Relation(_) => panic!("No json encoding for relations"),
        }
    }
}

impl FromJson for Value {
    fn from_json(json: &Json) -> Self {
        match *json {
            Json::String(ref string) => Value::String(string.clone()),
            Json::F64(float) => Value::Float(float),
            Json::I64(int) => Value::Float(int.to_f64().unwrap()),
            Json::U64(uint) => Value::Float(uint.to_f64().unwrap()),
            Json::Array(ref array) => Value::Tuple(array.iter().map(|j| Value::from_json(j)).collect()),
            _ => panic!("Cannot decode {:?} as Value", json),
        }
    }
}

impl<T: FromJson> FromJson for Vec<T> {
    fn from_json(json: &Json) -> Self {
        json.as_array().unwrap().iter().map(FromJson::from_json).collect()
    }
}

pub struct Event {
    changes: Changes,
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        Json::Object(vec![
            ("changes".to_string(), Json::Object(
                self.changes.iter().map(|&(ref view_id, ref view_changes)| {
                    (view_id.to_string(), Json::Object(vec![
                        ("inserted".to_string(), view_changes.inserted.to_json()),
                        ("removed".to_string(), view_changes.removed.to_json()),
                        ].into_iter().collect()))
                }).collect()))].into_iter().collect())
    }
}

impl FromJson for Event {
    fn from_json(json: &Json) -> Self {
        Event{
            changes: json.as_object().unwrap()["changes"]
            .as_object().unwrap().iter().map(|(view_id, view_changes)| {
                (view_id.to_string(), index::Changes{
                    inserted: FromJson::from_json(&view_changes.as_object().unwrap()["inserted"]),
                    removed: FromJson::from_json(&view_changes.as_object().unwrap()["removed"]),
                })
            }).collect()
        }
    }
}

struct Instance {
    input: World,
    flow: Flow,
    output: FlowState,
}

impl Instance {
    pub fn change(&mut self, changes: Changes) -> Changes {
        self.input.change(changes);
        let mut input_clone = self.input.clone();
        let (flow, mut output) = compile(&mut input_clone);
        flow.run(&mut output);
        let changes = flow.changes_since(&output, &self.flow, &self.output);
        self.flow = flow;
        self.output = output;
        changes
    }
}

// TODO holy crap why is everything blocking? this is a mess
pub fn serve() -> (mpsc::Receiver<Event>, mpsc::Receiver<sender::Sender<WebSocketStream>>) {
    let (input_sender, input_receiver) = mpsc::channel();
    let (sender_sender, sender_receiver) = mpsc::channel();
    thread::spawn(move || {
        let server = Server::bind("127.0.0.1:2794").unwrap();
        for connection in server {
            let input_sender = input_sender.clone();
            let sender_sender = sender_sender.clone();
            thread::spawn(move || {
                // accept request
                let request = connection.unwrap().read_request().unwrap();
                request.validate().unwrap();
                let response = request.accept();
                let (mut sender, mut receiver) = response.send().unwrap().split();

                let ip = sender.get_mut().peer_addr().unwrap();
                println!("Connection from {}", ip);
                ::std::io::stdout().flush().unwrap(); // TODO is this actually necessary?

                // hand over sender
                sender_sender.send(sender).unwrap();

                // handle messages
                for message in receiver.incoming_messages() {
                    let message = message.unwrap();
                    match message {
                        Message::Text(text) => {
                            let json = Json::from_str(&text).unwrap();
                            let event = FromJson::from_json(&json);
                            input_sender.send(event).unwrap();
                        }
                        _ => panic!("Unknown message: {:?}", message)
                    }
                }
            });
        }
    });
    (input_receiver, sender_receiver)
}

pub fn run() {
    let empty_world = World{views: HashMap::new()};
    let empty_flow = Flow{nodes: Vec::new()};
    let empty_output = FlowState{outputs: Vec::new(), dirty: BitSet::new()};
    let mut instance = Instance{
        input: empty_world,
        flow: empty_flow.clone(),
        output: empty_output.clone(),
    };
    let mut senders: Vec<sender::Sender<_>> = Vec::new();
    let (input_receiver, sender_receiver) = serve();
    loop {
        select!(
            input = input_receiver.recv() => {
                let input = input.unwrap();
                let changes = instance.change(input.changes);
                let text = format!("{}", Event{changes: changes}.to_json());
                for sender in senders.iter_mut() {
                    sender.send_message(Message::Text(text.clone())).unwrap();
                }
            },
            sender = sender_receiver.recv() => {
                let mut sender = sender.unwrap();
                let changes = instance.flow.changes_since(&instance.output, &empty_flow, &empty_output);
                let text = format!("{}", Event{changes: changes}.to_json());
                sender.send_message(Message::Text(text)).unwrap();
                senders.push(sender)
            }
            )
    }
}<commit_msg>Encode/decode relations<commit_after>use std::thread;
use std::sync::mpsc;
use websocket::{Server, Message, Sender, Receiver};
use websocket::server::sender;
use websocket::stream::WebSocketStream;
use std::collections::{HashMap, BitSet};
use std::io::prelude::*;
use std::num::ToPrimitive;

use value::{Value, Tuple};
use index;
use flow::{Changes, FlowState, Flow};
use compiler::{compile, World};
use rustc_serialize::json::{Json, ToJson};

trait FromJson {
    fn from_json(json: &Json) -> Self;
}

impl ToJson for Value {
    fn to_json(&self) -> Json {
        match *self {
            Value::String(ref string) => Json::String(string.clone()),
            Value::Float(float) => Json::F64(float),
            Value::Tuple(ref tuple) => tuple.to_json(),
            Value::Relation(ref relation) => Json::Object(vec![
                ("relation".to_string(), relation.iter().map(ToJson::to_json).collect::<Vec<_>>().to_json())
                ].into_iter().collect()),
        }
    }
}

impl FromJson for Value {
    fn from_json(json: &Json) -> Self {
        match *json {
            Json::String(ref string) => Value::String(string.clone()),
            Json::F64(float) => Value::Float(float),
            Json::I64(int) => Value::Float(int.to_f64().unwrap()),
            Json::U64(uint) => Value::Float(uint.to_f64().unwrap()),
            Json::Array(ref array) => Value::Tuple(array.iter().map(|j| Value::from_json(j)).collect()),
            Json::Object(ref object) => {
                assert!(object.len() == 1);
                let relation: Vec<Tuple> = FromJson::from_json(&object["relation"]);
                Value::Relation(relation.into_iter().collect())
            },
            _ => panic!("Cannot decode {:?} as Value", json),
        }
    }
}

impl<T: FromJson> FromJson for Vec<T> {
    fn from_json(json: &Json) -> Self {
        json.as_array().unwrap().iter().map(FromJson::from_json).collect()
    }
}

pub struct Event {
    changes: Changes,
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        Json::Object(vec![
            ("changes".to_string(), Json::Object(
                self.changes.iter().map(|&(ref view_id, ref view_changes)| {
                    (view_id.to_string(), Json::Object(vec![
                        ("inserted".to_string(), view_changes.inserted.to_json()),
                        ("removed".to_string(), view_changes.removed.to_json()),
                        ].into_iter().collect()))
                }).collect()))].into_iter().collect())
    }
}

impl FromJson for Event {
    fn from_json(json: &Json) -> Self {
        Event{
            changes: json.as_object().unwrap()["changes"]
            .as_object().unwrap().iter().map(|(view_id, view_changes)| {
                (view_id.to_string(), index::Changes{
                    inserted: FromJson::from_json(&view_changes.as_object().unwrap()["inserted"]),
                    removed: FromJson::from_json(&view_changes.as_object().unwrap()["removed"]),
                })
            }).collect()
        }
    }
}

struct Instance {
    input: World,
    flow: Flow,
    output: FlowState,
}

impl Instance {
    pub fn change(&mut self, changes: Changes) -> Changes {
        self.input.change(changes);
        let mut input_clone = self.input.clone();
        let (flow, mut output) = compile(&mut input_clone);
        flow.run(&mut output);
        let changes = flow.changes_since(&output, &self.flow, &self.output);
        self.flow = flow;
        self.output = output;
        changes
    }
}

// TODO holy crap why is everything blocking? this is a mess
pub fn serve() -> (mpsc::Receiver<Event>, mpsc::Receiver<sender::Sender<WebSocketStream>>) {
    let (input_sender, input_receiver) = mpsc::channel();
    let (sender_sender, sender_receiver) = mpsc::channel();
    thread::spawn(move || {
        let server = Server::bind("127.0.0.1:2794").unwrap();
        for connection in server {
            let input_sender = input_sender.clone();
            let sender_sender = sender_sender.clone();
            thread::spawn(move || {
                // accept request
                let request = connection.unwrap().read_request().unwrap();
                request.validate().unwrap();
                let response = request.accept();
                let (mut sender, mut receiver) = response.send().unwrap().split();

                let ip = sender.get_mut().peer_addr().unwrap();
                println!("Connection from {}", ip);
                ::std::io::stdout().flush().unwrap(); // TODO is this actually necessary?

                // hand over sender
                sender_sender.send(sender).unwrap();

                // handle messages
                for message in receiver.incoming_messages() {
                    let message = message.unwrap();
                    match message {
                        Message::Text(text) => {
                            let json = Json::from_str(&text).unwrap();
                            let event = FromJson::from_json(&json);
                            input_sender.send(event).unwrap();
                        }
                        _ => panic!("Unknown message: {:?}", message)
                    }
                }
            });
        }
    });
    (input_receiver, sender_receiver)
}

pub fn run() {
    let empty_world = World{views: HashMap::new()};
    let empty_flow = Flow{nodes: Vec::new()};
    let empty_output = FlowState{outputs: Vec::new(), dirty: BitSet::new()};
    let mut instance = Instance{
        input: empty_world,
        flow: empty_flow.clone(),
        output: empty_output.clone(),
    };
    let mut senders: Vec<sender::Sender<_>> = Vec::new();
    let (input_receiver, sender_receiver) = serve();
    loop {
        select!(
            input = input_receiver.recv() => {
                let input = input.unwrap();
                let changes = instance.change(input.changes);
                let text = format!("{}", Event{changes: changes}.to_json());
                for sender in senders.iter_mut() {
                    sender.send_message(Message::Text(text.clone())).unwrap();
                }
            },
            sender = sender_receiver.recv() => {
                let mut sender = sender.unwrap();
                let changes = instance.flow.changes_since(&instance.output, &empty_flow, &empty_output);
                let text = format!("{}", Event{changes: changes}.to_json());
                sender.send_message(Message::Text(text)).unwrap();
                senders.push(sender)
            }
            )
    }
}<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::serde::{from_dynamic, to_dynamic};
use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::Value as Json;

pub(crate) struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &[PathAndJson<'reg, 'rc>],
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
    let params: Dynamic = to_dynamic(params.iter().map(|p| p.value()).collect::<Vec<&Json>>())?;

    let hash: Dynamic = to_dynamic(
        hash.iter()
            .map(|(k, v)| ((*k).to_owned(), v.value()))
            .collect::<HashMap<String, &Json>>(),
    )?;

    let mut scope = Scope::new();
    scope.push_dynamic("params", params);
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    let result_json: Json = from_dynamic(&result)?;

    Ok(Some(ScopedJson::Derived(result_json)))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = to_dynamic(&j0).unwrap();
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = to_dynamic(&j1).unwrap();
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_to_json() {
        let d0 = Dynamic::from("tomcat".to_owned());

        assert_eq!(
            Json::String("tomcat".to_owned()),
            from_dynamic::<Json>(&d0).unwrap()
        );
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<commit_msg>(fix) update return type of script helper<commit_after>use std::collections::{BTreeMap, HashMap};

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::serde::{from_dynamic, to_dynamic};
use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::Value as Json;

pub(crate) struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &[PathAndJson<'reg, 'rc>],
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
    let params: Dynamic = to_dynamic(params.iter().map(|p| p.value()).collect::<Vec<&Json>>())?;

    let hash: Dynamic = to_dynamic(
        hash.iter()
            .map(|(k, v)| ((*k).to_owned(), v.value()))
            .collect::<HashMap<String, &Json>>(),
    )?;

    let mut scope = Scope::new();
    scope.push_dynamic("params", params);
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    let result_json: Json = from_dynamic(&result)?;

    Ok(ScopedJson::Derived(result_json))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<ScopedJson<'reg, 'rc>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = to_dynamic(&j0).unwrap();
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = to_dynamic(&j1).unwrap();
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_to_json() {
        let d0 = Dynamic::from("tomcat".to_owned());

        assert_eq!(
            Json::String("tomcat".to_owned()),
            from_dynamic::<Json>(&d0).unwrap()
        );
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<|endoftext|>


<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>,
    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();

                    // Opcode
                    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 => {}
                    }

                    // Operand A
                    match bytecodes.next() {
                        Some(operand) => {
                            match self.labels.get(operand) {
                                None => {
                                    match u32::from_str_radix(operand, 16) {
                                        Ok(bytecode) => {
                                            self.bytecodes.push(bytecode);
                                        }
                                        _ => {}
                                    }
                                }
                                Some(address) => {
                                    self.bytecodes.push(*address);
                                }
                            }
                        }
                        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");

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

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

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

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

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

    #[test]
    fn it_parses_operand_A_as_a_label() {
        let mut compiler = Compiler::new();
        compiler.parse("brk end\nend:");

        assert_eq!(compiler.bytecodes, vec![0x0, 0x4]);
    }
}
<commit_msg>Compile operands B and C.<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();

                    // Opcode
                    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 => {}
                    }

                    // Operand A
                    match bytecodes.next() {
                        Some(operand) => {
                            match self.labels.get(operand) {
                                None => {
                                    match u32::from_str_radix(operand, 16) {
                                        Ok(bytecode) => {
                                            self.bytecodes.push(bytecode);
                                        }
                                        _ => {}
                                    }
                                }
                                Some(address) => {
                                    self.bytecodes.push(*address);
                                }
                            }
                        }
                        None => {}
                    }

                    // Operand B
                    match bytecodes.next() {
                        Some(operand) => {
                            match self.labels.get(operand) {
                                None => {
                                    match u32::from_str_radix(operand, 16) {
                                        Ok(bytecode) => {
                                            self.bytecodes.push(bytecode);
                                        }
                                        _ => {}
                                    }
                                }
                                Some(address) => {
                                    self.bytecodes.push(*address);
                                }
                            }
                        }
                        None => {}
                    }

                    // Operand C
                    match bytecodes.next() {
                        Some(operand) => {
                            match self.labels.get(operand) {
                                None => {
                                    match u32::from_str_radix(operand, 16) {
                                        Ok(bytecode) => {
                                            self.bytecodes.push(bytecode);
                                        }
                                        _ => {}
                                    }
                                }
                                Some(address) => {
                                    self.bytecodes.push(*address);
                                }
                            }
                        }
                        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");

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

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

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

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

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

    #[test]
    fn it_parses_operand_A_as_a_label() {
        let mut compiler = Compiler::new();
        compiler.parse("brk end\nend:");

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

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

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

    #[test]
    fn it_parses_operand_B_as_a_label() {
        let mut compiler = Compiler::new();
        compiler.parse("brk f end\nend:");

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

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

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

    #[test]
    fn it_parses_operand_C_as_a_label() {
        let mut compiler = Compiler::new();
        compiler.parse("brk f f end\nend:");

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