<commit_before>use crate::support::{database, project};

#[test]
fn migration_redo_runs_the_last_migration_down_and_up() {
    let p = project("migration_redo").folder("migrations").build();
    p.create_migration(
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn migration_redo_runs_the_last_two_migrations_down_and_up() {
    let p = project("migration_redo_runs_the_last_two_migrations_down_and_up")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    // Redo the last two migration files. The `contracts` and `bills` tables should be re-runs.
    // The `customers` table shouldn't be redo.
    let result = p
        .command("migration")
        .arg("redo")
        .arg("-n")
        .arg("2")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Running migration 2017-09-12-210424_create_bills\n\
                Running migration 2017-09-03-210424_create_contracts\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));
}

#[test]
fn migration_redo_respects_migration_dir_var() {
    let p = project("migration_redo_var").folder("foo").build();

    p.create_migration_in_directory(
        "foo",
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").arg("--migration-dir=foo").run();

    let result = p
        .command("migration")
        .arg("redo")
        .arg("--migration-dir=foo")
        .run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn migration_redo_respects_migration_dir_env() {
    let p = project("migration_redo_env").folder("bar").build();

    p.create_migration_in_directory(
        "bar",
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").arg("--migration-dir=bar").run();

    let result = p
        .command("migration")
        .arg("redo")
        .env("MIGRATION_DIRECTORY", "bar")
        .run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn output_contains_path_to_migration_script() {
    let p = project("output_contains_path_to_migration_script")
        .folder("migrations")
        .build();
    p.create_migration(
        "output_contains_path_to_migration_script",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users};",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    assert!(!result.is_success(), "Result was successful {:?}", result);
    assert!(
        result.stdout().contains("down.sql"),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn error_migrations_fails() {
    let p = project("redo_error_migrations_fails")
        .folder("migrations")
        .build();
    p.create_migration(
        "redo_error_migrations_fails",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users};",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    assert!(!result.is_success());
    assert!(result.stderr().contains("Failed with: "));
}

#[test]
fn migration_redo_respects_migrations_dir_from_diesel_toml() {
    let p = project("migration_redo_respects_migrations_dir_from_diesel_toml")
        .folder("custom_migrations")
        .file(
            "diesel.toml",
            r#"
            [migrations_directory]
            dir = "custom_migrations"
            "#,
        )
        .build();

    p.create_migration_in_directory(
        "custom_migrations",
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn migration_redo_all_runs_all_migrations_down_and_up() {
    let p = project("migration_redo_all_runs_all_migrations_down_and_up")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p.command("migration").arg("redo").arg("--all").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n\
                Running migration 2017-09-12-210424_create_bills\n\
                Running migration 2017-09-03-210424_create_contracts\n\
                Running migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));
}

#[test]
fn migration_redo_with_more_than_max_should_redo_all() {
    let p = project("migration_redo_with_more_than_max_should_redo_all")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p
        .command("migration")
        .arg("redo")
        .arg("-n")
        .arg("1000")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n\
                Running migration 2017-09-12-210424_create_bills\n\
                Running migration 2017-09-03-210424_create_contracts\n\
                Running migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));
}

#[test]
fn migration_redo_n_with_a_string_should_throw_an_error() {
    let p = project("migration_redo_n_with_a_string_should_throw_an_error")
        .folder("migrations")
        .build();

    // Make sure the project is setup
    p.command("setup").run();

    // Should not revert any migration.
    let result = p
        .command("migration")
        .arg("redo")
        .arg("-n")
        .arg("infinite")
        .run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stderr() == "error: Invalid value for '--number <REDO_NUMBER>': infinite isn't a positive integer.\n",
        "Unexpected stderr : {}",
        result.stderr()
    );
}

#[test]
fn migration_redo_with_zero_should_not_revert_any_migration() {
    let p = project("migration_redo_with_zero_should_not_revert_any_migration")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));

    // Should not revert any migration.
    let result = p
        .command("migration")
        .arg("redo")
        .arg("-n")
        .arg("0")
        .run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(result.stdout() == "");
}
<commit_msg>Fix rustfmt<commit_after>use crate::support::{database, project};

#[test]
fn migration_redo_runs_the_last_migration_down_and_up() {
    let p = project("migration_redo").folder("migrations").build();
    p.create_migration(
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn migration_redo_runs_the_last_two_migrations_down_and_up() {
    let p = project("migration_redo_runs_the_last_two_migrations_down_and_up")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    // Redo the last two migration files. The `contracts` and `bills` tables should be re-runs.
    // The `customers` table shouldn't be redo.
    let result = p.command("migration").arg("redo").arg("-n").arg("2").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Running migration 2017-09-12-210424_create_bills\n\
                Running migration 2017-09-03-210424_create_contracts\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));
}

#[test]
fn migration_redo_respects_migration_dir_var() {
    let p = project("migration_redo_var").folder("foo").build();

    p.create_migration_in_directory(
        "foo",
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").arg("--migration-dir=foo").run();

    let result = p
        .command("migration")
        .arg("redo")
        .arg("--migration-dir=foo")
        .run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn migration_redo_respects_migration_dir_env() {
    let p = project("migration_redo_env").folder("bar").build();

    p.create_migration_in_directory(
        "bar",
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").arg("--migration-dir=bar").run();

    let result = p
        .command("migration")
        .arg("redo")
        .env("MIGRATION_DIRECTORY", "bar")
        .run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn output_contains_path_to_migration_script() {
    let p = project("output_contains_path_to_migration_script")
        .folder("migrations")
        .build();
    p.create_migration(
        "output_contains_path_to_migration_script",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users};",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    assert!(!result.is_success(), "Result was successful {:?}", result);
    assert!(
        result.stdout().contains("down.sql"),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn error_migrations_fails() {
    let p = project("redo_error_migrations_fails")
        .folder("migrations")
        .build();
    p.create_migration(
        "redo_error_migrations_fails",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users};",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    assert!(!result.is_success());
    assert!(result.stderr().contains("Failed with: "));
}

#[test]
fn migration_redo_respects_migrations_dir_from_diesel_toml() {
    let p = project("migration_redo_respects_migrations_dir_from_diesel_toml")
        .folder("custom_migrations")
        .file(
            "diesel.toml",
            r#"
            [migrations_directory]
            dir = "custom_migrations"
            "#,
        )
        .build();

    p.create_migration_in_directory(
        "custom_migrations",
        "12345_create_users_table",
        "CREATE TABLE users (id INTEGER PRIMARY KEY);",
        "DROP TABLE users;",
    );

    // Make sure the project is setup
    p.command("setup").run();

    let result = p.command("migration").arg("redo").run();

    let expected_stdout = "\
Rolling back migration 12345_create_users_table
Running migration 12345_create_users_table
";

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains(expected_stdout),
        "Unexpected stdout {}",
        result.stdout()
    );
}

#[test]
fn migration_redo_all_runs_all_migrations_down_and_up() {
    let p = project("migration_redo_all_runs_all_migrations_down_and_up")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p.command("migration").arg("redo").arg("--all").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n\
                Running migration 2017-09-12-210424_create_bills\n\
                Running migration 2017-09-03-210424_create_contracts\n\
                Running migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));
}

#[test]
fn migration_redo_with_more_than_max_should_redo_all() {
    let p = project("migration_redo_with_more_than_max_should_redo_all")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p
        .command("migration")
        .arg("redo")
        .arg("-n")
        .arg("1000")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n\
                Running migration 2017-09-12-210424_create_bills\n\
                Running migration 2017-09-03-210424_create_contracts\n\
                Running migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));
}

#[test]
fn migration_redo_n_with_a_string_should_throw_an_error() {
    let p = project("migration_redo_n_with_a_string_should_throw_an_error")
        .folder("migrations")
        .build();

    // Make sure the project is setup
    p.command("setup").run();

    // Should not revert any migration.
    let result = p
        .command("migration")
        .arg("redo")
        .arg("-n")
        .arg("infinite")
        .run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stderr() == "error: Invalid value for '--number <REDO_NUMBER>': infinite isn't a positive integer.\n",
        "Unexpected stderr : {}",
        result.stderr()
    );
}

#[test]
fn migration_redo_with_zero_should_not_revert_any_migration() {
    let p = project("migration_redo_with_zero_should_not_revert_any_migration")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));

    // Should not revert any migration.
    let result = p.command("migration").arg("redo").arg("-n").arg("0").run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(result.stdout() == "");
}
<|endoftext|>


<commit_before>use std::process::exit;

use libimagdiary::diary::Diary;
use libimagentryview::viewer::Viewer;
use libimagentryview::builtin::plain::PlainViewer;
use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

use util::get_diary_name;

pub fn view(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary name");
        exit(1);
    }
    let diaryname = diaryname.unwrap();
    let diary = Diary::open(rt.store(), &diaryname[..]);
    let show_header = rt.cli().subcommand_matches("view").unwrap().is_present("show-header");

    match diary.entries() {
        Ok(entries) => {
            let pv = PlainViewer::new(show_header);
            for entry in entries.into_iter().filter_map(Result::ok) {
                let id = entry.diary_id();
                println!("{} :\n", id);
                pv.view_entry(&entry);
                println!("\n---\n");
            }
        },
        Err(e) => trace_error(&e),
    }
}

<commit_msg>If an error happens while viewing, trace it<commit_after>use std::process::exit;

use libimagdiary::diary::Diary;
use libimagentryview::viewer::Viewer;
use libimagentryview::builtin::plain::PlainViewer;
use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

use util::get_diary_name;

pub fn view(rt: &Runtime) {
    let diaryname = get_diary_name(rt);
    if diaryname.is_none() {
        warn!("No diary name");
        exit(1);
    }
    let diaryname = diaryname.unwrap();
    let diary = Diary::open(rt.store(), &diaryname[..]);
    let show_header = rt.cli().subcommand_matches("view").unwrap().is_present("show-header");

    match diary.entries() {
        Ok(entries) => {
            let pv = PlainViewer::new(show_header);
            for entry in entries.into_iter().filter_map(Result::ok) {
                let id = entry.diary_id();
                println!("{} :\n", id);
                if let Err(e) = pv.view_entry(&entry) {
                    trace_error(&e);
                };
                println!("\n---\n");
            }
        },
        Err(e) => trace_error(&e),
    }
}

<|endoftext|>


<commit_before>extern crate clap;

use clap::Parser;

use std::collections::HashSet;
use std::fmt;
use std::time::Instant;

type Piece = Vec<[i32; 3]>;
type Puzzle = Vec<Piece>;

const SIN: [i32; 4] = [0, 1, 0, -1];
const COS: [i32; 4] = [1, 0, -1, 0];

/// Program to display the 3x3 cube solution(s).
/// Solves mintaur by default.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    /// Solve the blue problem.
    #[arg(short, long)]
    blue: bool,
}

#[derive(Clone, Debug)]
struct PuzzleDense {
    data: [[[i32; 3]; 3]; 3],
}

impl fmt::Display for PuzzleDense {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{:?}", self.data[0])?;
        writeln!(f, "{:?}", self.data[1])?;
        writeln!(f, "{:?}", self.data[2])
    }
}

fn main() {
    let now = Instant::now();

    let puzzle;
    let args = Args::parse();
    if args.blue {
        puzzle = blue();
    } else {
        puzzle = minotaur()
    }

    let pieces = push_to_zero(puzzle);
    for piece in &pieces {
        for part in piece {
            println!("{:?}", part);
        }
        println!();
    }

    let zero = vec![PuzzleDense { data: zeros() }];

    let mut solutions = Vec::new();
    for (i, piece) in pieces.iter().enumerate() {
        if i == 0 {
            solutions = all_puts(zero.clone(), (i + 1) as i32, piece);
        } else {
            solutions = all_rotations_and_puts(solutions.clone(), (i + 1) as i32, piece);
        }
    }

    for solution in solutions {
        println!("{:}", solution);
    }

    let elapsed_time = now.elapsed();
    println!("Running the program took {} seconds.", elapsed_time.as_millis() as f64 / 1000.0);
}

fn all_rotations_and_puts(
    already_placed: Vec<PuzzleDense>,
    piece_count: i32,
    piece: &Piece,
) -> Vec<PuzzleDense> {
    let mut solutions = Vec::new();
    for rotation in all_rotations(piece) {
        for s in all_puts(already_placed.clone(), piece_count, &rotation) {
            solutions.push(s);
        }
    }
    solutions
}

fn all_rotations(piece: &Piece) -> Vec<Vec<[i32; 3]>> {
    let mut all_rots = Vec::new();
    for theta in 0..4 {
        let mut rotations = Vec::new();
        for [x, y, z] in piece {
            let rotated_z = [
                x * COS[theta] - y * SIN[theta],
                x * SIN[theta] + y * COS[theta],
                *z,
            ];
            rotations.push(rotated_z);
        }
        all_rots.push(rotations);
    }

    let mut all_rotss = all_rots.clone();
    for piece in &all_rots {
        for theta in 0..4 {
            let mut rotations = Vec::new();
            for [x, y, z] in piece {
                let rotated_yz = [
                    x * COS[theta] + z * SIN[theta],
                    *y,
                    -x * SIN[theta] + z * COS[theta],
                ];
                rotations.push(rotated_yz);
            }
            all_rotss.push(rotations);
        }
    }

    let mut all_rotsss = all_rotss.clone();
    for piece in &all_rotss {
        for theta in 0..4 {
            let mut rotations = Vec::new();
            for [x, y, z] in piece {
                let rotated_xyz = [
                    *x,
                    y * COS[theta] - z * SIN[theta],
                    y * SIN[theta] + z * COS[theta],
                ];
                rotations.push(rotated_xyz);
            }
            all_rotsss.push(rotations)
        }
    }

    let mut unique_solutions = HashSet::new();
    let rots = push_to_zero(all_rotsss);
    for mut solution in rots {
        solution.sort();
        unique_solutions.insert(solution);
    }

    let mut rotations = Vec::new();
    for solution in unique_solutions {
        rotations.push(solution);
    }
    rotations
}

fn max_xyz(piece: &Piece) -> (i32, i32, i32) {
    let mut max_x = -1;
    let mut max_y = -1;
    let mut max_z = -1;

    for [x, y, z] in piece {
        if x > &max_x {
            max_x = *x;
        }
        if y > &max_y {
            max_y = *y;
        }
        if z > &max_z {
            max_z = *z;
        }
    }
    (max_x, max_y, max_z)
}

fn all_puts(already_placed: Vec<PuzzleDense>, piece_count: i32, piece: &Piece) -> Vec<PuzzleDense> {
    let mut all_solutions = Vec::new();
    let (max_x, max_y, max_z) = max_xyz(piece);

    for x_step in 0..3 - max_x {
        for y_step in 0..3 - max_y {
            for z_step in 0..3 - max_z {
                let already_placed = already_placed.clone();
                'next_piece: for mut puzzle in already_placed {
                    for [x, y, z] in piece {
                        if puzzle.data[(x + x_step) as usize][(y + y_step) as usize]
                            [(z + z_step) as usize]
                            > 0
                        {
                            continue 'next_piece;
                        }
                    }

                    for [x, y, z] in piece {
                        puzzle.data[(x + x_step) as usize][(y + y_step) as usize]
                            [(z + z_step) as usize] = piece_count;
                    }

                    all_solutions.push(puzzle);
                }
            }
        }
    }

    all_solutions
}

fn zeros() -> [[[i32; 3]; 3]; 3] {
    [
        [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
        [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
        [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
    ]
}

fn push_to_zero(puzzle: Puzzle) -> Puzzle {
    let mut pieces = Vec::new();

    for part in &puzzle {
        let mut piece = Vec::new();
        let mut min_x = 99;
        let mut min_y = 99;
        let mut min_z = 99;

        for [x, y, z] in part {
            if x < &min_x {
                min_x = *x;
            }
            if y < &min_y {
                min_y = *y;
            }
            if z < &min_z {
                min_z = *z;
            }
        }
        for [x, y, z] in part {
            piece.push([x - min_x, y - min_y, z - min_z]);
        }

        pieces.push(piece);
    }

    pieces
}

fn blue() -> Puzzle {
    let mut blue = Vec::new();

    blue.push(vec![[2, 0, 2], [2, 1, 2], [2, 2, 2]]);

    let piece_2 = vec![[1, 0, 2], [1, 1, 1], [1, 1, 2], [2, 0, 2]];
    for _ in 0..3 {
        blue.push(piece_2.clone());
    }

    let piece_3 = vec![[1, 0, 2], [1, 1, 2], [2, 0, 1], [2, 0, 2]];
    for _ in 0..3 {
        blue.push(piece_3.clone());
    }

    blue
}

fn minotaur() -> Puzzle {
    let mut minotaur = Vec::new();
    minotaur.push(vec![[1, 2, 1], [2, 2, 1], [2, 1, 1], [2, 1, 2]]);
    minotaur.push(vec![[1, 2, 1], [2, 2, 1], [2, 2, 2], [2, 1, 2]]);
    minotaur.push(vec![[1, 1, 1], [2, 1, 1], [3, 1, 1], [2, 2, 1]]);
    minotaur.push(vec![[1, 1, 2], [1, 2, 2], [1, 2, 1], [1, 3, 1], [2, 2, 1]]);
    minotaur.push(vec![[1, 3, 1], [2, 1, 2], [2, 2, 2], [2, 2, 1], [2, 3, 1]]);
    minotaur.push(vec![[1, 1, 1], [1, 2, 1], [1, 3, 1], [2, 1, 1], [1, 2, 2]]);
    minotaur
}<commit_msg>Add semicolon.<commit_after>extern crate clap;

use clap::Parser;

use std::collections::HashSet;
use std::fmt;
use std::time::Instant;

type Piece = Vec<[i32; 3]>;
type Puzzle = Vec<Piece>;

const SIN: [i32; 4] = [0, 1, 0, -1];
const COS: [i32; 4] = [1, 0, -1, 0];

/// Program to display the 3x3 cube solution(s).
/// Solves mintaur by default.
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    /// Solve the blue problem.
    #[arg(short, long)]
    blue: bool,
}

#[derive(Clone, Debug)]
struct PuzzleDense {
    data: [[[i32; 3]; 3]; 3],
}

impl fmt::Display for PuzzleDense {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{:?}", self.data[0])?;
        writeln!(f, "{:?}", self.data[1])?;
        writeln!(f, "{:?}", self.data[2])
    }
}

fn main() {
    let now = Instant::now();

    let puzzle;
    let args = Args::parse();
    if args.blue {
        puzzle = blue();
    } else {
        puzzle = minotaur();
    }

    let pieces = push_to_zero(puzzle);
    for piece in &pieces {
        for part in piece {
            println!("{:?}", part);
        }
        println!();
    }

    let zero = vec![PuzzleDense { data: zeros() }];

    let mut solutions = Vec::new();
    for (i, piece) in pieces.iter().enumerate() {
        if i == 0 {
            solutions = all_puts(zero.clone(), (i + 1) as i32, piece);
        } else {
            solutions = all_rotations_and_puts(solutions.clone(), (i + 1) as i32, piece);
        }
    }

    for solution in solutions {
        println!("{:}", solution);
    }

    let elapsed_time = now.elapsed();
    println!("Running the program took {} seconds.", elapsed_time.as_millis() as f64 / 1000.0);
}

fn all_rotations_and_puts(
    already_placed: Vec<PuzzleDense>,
    piece_count: i32,
    piece: &Piece,
) -> Vec<PuzzleDense> {
    let mut solutions = Vec::new();
    for rotation in all_rotations(piece) {
        for s in all_puts(already_placed.clone(), piece_count, &rotation) {
            solutions.push(s);
        }
    }
    solutions
}

fn all_rotations(piece: &Piece) -> Vec<Vec<[i32; 3]>> {
    let mut all_rots = Vec::new();
    for theta in 0..4 {
        let mut rotations = Vec::new();
        for [x, y, z] in piece {
            let rotated_z = [
                x * COS[theta] - y * SIN[theta],
                x * SIN[theta] + y * COS[theta],
                *z,
            ];
            rotations.push(rotated_z);
        }
        all_rots.push(rotations);
    }

    let mut all_rotss = all_rots.clone();
    for piece in &all_rots {
        for theta in 0..4 {
            let mut rotations = Vec::new();
            for [x, y, z] in piece {
                let rotated_yz = [
                    x * COS[theta] + z * SIN[theta],
                    *y,
                    -x * SIN[theta] + z * COS[theta],
                ];
                rotations.push(rotated_yz);
            }
            all_rotss.push(rotations);
        }
    }

    let mut all_rotsss = all_rotss.clone();
    for piece in &all_rotss {
        for theta in 0..4 {
            let mut rotations = Vec::new();
            for [x, y, z] in piece {
                let rotated_xyz = [
                    *x,
                    y * COS[theta] - z * SIN[theta],
                    y * SIN[theta] + z * COS[theta],
                ];
                rotations.push(rotated_xyz);
            }
            all_rotsss.push(rotations)
        }
    }

    let mut unique_solutions = HashSet::new();
    let rots = push_to_zero(all_rotsss);
    for mut solution in rots {
        solution.sort();
        unique_solutions.insert(solution);
    }

    let mut rotations = Vec::new();
    for solution in unique_solutions {
        rotations.push(solution);
    }
    rotations
}

fn max_xyz(piece: &Piece) -> (i32, i32, i32) {
    let mut max_x = -1;
    let mut max_y = -1;
    let mut max_z = -1;

    for [x, y, z] in piece {
        if x > &max_x {
            max_x = *x;
        }
        if y > &max_y {
            max_y = *y;
        }
        if z > &max_z {
            max_z = *z;
        }
    }
    (max_x, max_y, max_z)
}

fn all_puts(already_placed: Vec<PuzzleDense>, piece_count: i32, piece: &Piece) -> Vec<PuzzleDense> {
    let mut all_solutions = Vec::new();
    let (max_x, max_y, max_z) = max_xyz(piece);

    for x_step in 0..3 - max_x {
        for y_step in 0..3 - max_y {
            for z_step in 0..3 - max_z {
                let already_placed = already_placed.clone();
                'next_piece: for mut puzzle in already_placed {
                    for [x, y, z] in piece {
                        if puzzle.data[(x + x_step) as usize][(y + y_step) as usize]
                            [(z + z_step) as usize]
                            > 0
                        {
                            continue 'next_piece;
                        }
                    }

                    for [x, y, z] in piece {
                        puzzle.data[(x + x_step) as usize][(y + y_step) as usize]
                            [(z + z_step) as usize] = piece_count;
                    }

                    all_solutions.push(puzzle);
                }
            }
        }
    }

    all_solutions
}

fn zeros() -> [[[i32; 3]; 3]; 3] {
    [
        [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
        [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
        [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
    ]
}

fn push_to_zero(puzzle: Puzzle) -> Puzzle {
    let mut pieces = Vec::new();

    for part in &puzzle {
        let mut piece = Vec::new();
        let mut min_x = 99;
        let mut min_y = 99;
        let mut min_z = 99;

        for [x, y, z] in part {
            if x < &min_x {
                min_x = *x;
            }
            if y < &min_y {
                min_y = *y;
            }
            if z < &min_z {
                min_z = *z;
            }
        }
        for [x, y, z] in part {
            piece.push([x - min_x, y - min_y, z - min_z]);
        }

        pieces.push(piece);
    }

    pieces
}

fn blue() -> Puzzle {
    let mut blue = Vec::new();

    blue.push(vec![[2, 0, 2], [2, 1, 2], [2, 2, 2]]);

    let piece_2 = vec![[1, 0, 2], [1, 1, 1], [1, 1, 2], [2, 0, 2]];
    for _ in 0..3 {
        blue.push(piece_2.clone());
    }

    let piece_3 = vec![[1, 0, 2], [1, 1, 2], [2, 0, 1], [2, 0, 2]];
    for _ in 0..3 {
        blue.push(piece_3.clone());
    }

    blue
}

fn minotaur() -> Puzzle {
    let mut minotaur = Vec::new();
    minotaur.push(vec![[1, 2, 1], [2, 2, 1], [2, 1, 1], [2, 1, 2]]);
    minotaur.push(vec![[1, 2, 1], [2, 2, 1], [2, 2, 2], [2, 1, 2]]);
    minotaur.push(vec![[1, 1, 1], [2, 1, 1], [3, 1, 1], [2, 2, 1]]);
    minotaur.push(vec![[1, 1, 2], [1, 2, 2], [1, 2, 1], [1, 3, 1], [2, 2, 1]]);
    minotaur.push(vec![[1, 3, 1], [2, 1, 2], [2, 2, 2], [2, 2, 1], [2, 3, 1]]);
    minotaur.push(vec![[1, 1, 1], [1, 2, 1], [1, 3, 1], [2, 1, 1], [1, 2, 2]]);
    minotaur
}<|endoftext|>


<commit_before>extern crate jmespath;

use std::fs;
use std::fs::File;
use std::io::Read;

use jmespath::RcVar;
use jmespath::RuntimeError;
use jmespath::Expression;

type AssertionBox = Box<TestAssertion>;

/// Performs a test-case assertion.
trait TestAssertion {
    /// Runs the assertion of a test case
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String>;

    /// Attempts to parse an expression for a case, returning the expression or an error string.
    fn try_parse<'a>(&self,
                     suite: &TestSuite,
                     case: &'a TestCase)
                     -> Result<Expression<'a>, String> {
        match Expression::new(&case.expression) {
            Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
            Ok(expr) => Ok(expr)
        }
    }

    /// Formats an error message for a test case failure.
    fn err_message(&self, suite: &TestSuite, case: &TestCase, message: String) -> String {
        format!("Test suite: {}\nExpression: {}\nExpected: {}\nResult: {}\n==============",
                 suite.filename, case.expression, case.expected_result, message).to_string()
    }
}

struct TestSuite {
    filename: String,
    given: RcVar,
    cases: Vec<TestCase>,
}

impl TestSuite {
    /// Creates a test suite from parsed JSON data.
    fn from_variable(filename: String, suite: &RcVar) -> TestSuite {
        TestSuite {
            filename: filename,
            given: suite.get_value("given").unwrap(),
            cases: suite.get_value("cases").unwrap()
                .as_array()
                .unwrap()
                .iter()
                .map(|v| TestCase::from_variable(v))
                .collect::<Vec<TestCase>>()
        }
    }
}

struct TestCase {
    expression: String,
    expected_result: String,
    assertion: AssertionBox
}

impl TestCase {
    /// Creates a test case from parsed JSON data.
    fn from_variable(case: &RcVar) -> TestCase {
        TestCase {
            expression: case.get_value("expression").unwrap().as_string().unwrap().clone(),
            expected_result: match case.get_value("error") {
                None => case.get_value("result").unwrap().to_string().unwrap(),
                Some(err) => err.as_string().unwrap().clone()
            },
            assertion: match case.get_value("error") {
                None => Box::new(ValidResult {
                    expected_result: case.get_value("result").unwrap()
                }),
                Some(err) => {
                    match err.as_string().unwrap().as_ref() {
                        "syntax" => Box::new(SyntaxError),
                        "invalid-type" => Box::new(InvalidType),
                        "invalid-value" => Box::new(InvalidSlice),
                        "invalid-arity" => Box::new(InvalidArity),
                        "unknown-function" => Box::new(UnknownFunction),
                        v @ _ => panic!("Unknown error type: {}", v),
                    }
                }
            }
        }
    }
}

/// Ensures that the expression fails due to an invalid-arity error.
struct InvalidArity;

impl TestAssertion for InvalidArity {
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String> {
        match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
            Err(RuntimeError::NotEnoughArguments{..}) => Ok(()),
            Err(RuntimeError::TooManyArguments{..}) => Ok(()),
            Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
            Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
        }
    }
}

/// Ensures that the expression fails due to an invalid-type error.
struct InvalidType;

impl TestAssertion for InvalidType {
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String> {
        match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
            Err(RuntimeError::InvalidType{..}) => Ok(()),
            Err(RuntimeError::InvalidReturnType{..}) => Ok(()),
            Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
            Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
        }
    }
}

/// Ensures that the expression fails due to an invalid-value error.
struct InvalidSlice;

impl TestAssertion for InvalidSlice {
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String> {
        match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
            Err(RuntimeError::InvalidSlice) => Ok(()),
            Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
            Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
        }
    }
}

/// Ensures that the expression fails due to an unknown-function error.
struct UnknownFunction;

impl TestAssertion for UnknownFunction {
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String> {
        match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
            Err(RuntimeError::UnknownFunction{..}) => Ok(()),
            Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
            Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
        }
    }
}

/// Ensures taht an expression cannot be parsed due to a syntax error.
struct SyntaxError;

impl TestAssertion for SyntaxError {
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String> {
        match Expression::new(&case.expression) {
            Err(_) => Ok(()),
            Ok(expr) => Err(self.err_message(suite, case, format!("Parsed {:?}", expr)))
        }
    }
}

/// Ensures that the expression is parsed and returns an expected result.
struct ValidResult {
    expected_result: RcVar
}

impl TestAssertion for ValidResult {
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String> {
        match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
            Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
            Ok(r) => {
                match r.as_string() {
                    Some(s) if s != self.expected_result.as_string().unwrap() => {
                        Err(self.err_message(suite, case, r.to_string().unwrap()))
                    },
                    Some(_) if r != self.expected_result => {
                        Err(self.err_message(suite, case, r.to_string().unwrap()))
                    },
                    _ => Ok(())
                }
            }
        }
    }
}

fn load_test_suites() -> Vec<TestSuite> {
    let mut result = vec![];
    let files = fs::read_dir("tests/compliance/").unwrap();
    for filename in files {
        let path = filename.unwrap().path();
        let file_path = path.to_str().unwrap().to_string();
        let mut f = File::open(path).unwrap();
        let mut file_data = String::new();
        f.read_to_string(&mut file_data).unwrap();
        let data = jmespath::Variable::from_str(&file_data).unwrap();
        let suites = data.as_array().unwrap();
        for suite in suites {
            result.push(TestSuite::from_variable(file_path.clone(), suite));
        }
    }
    result
}

#[test]
fn run_compliance() {
    let mut failures = 0;
    let suites = load_test_suites();
    for suite in suites.iter() {
        for case in suite.cases.iter() {
            if let Err(e) = case.assertion.assert(suite, case) {
                println!("{}\n", e);
                failures += 1;
            }
        }
    }
    if failures > 0 {
        panic!("{} compliance tests failed", failures);
    }
}
<commit_msg>Using enums for test assertions<commit_after>extern crate jmespath;

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

use jmespath::RcVar;
use jmespath::RuntimeError;
use jmespath::Expression;

/// Test case assertions.
enum Assertion {
    /// Ensures that the expression fails due to an invalid-arity error.
    InvalidArity,
    /// Ensures that the expression fails due to an invalid-type error.
    InvalidType,
    /// Ensures that the expression fails due to an invalid-value error.
    InvalidSlice,
    /// Ensures that the expression fails due to an unknown-function error.
    UnknownFunction,
    /// Ensures that an expression cannot be parsed due to a syntax error.
    SyntaxError,
    /// Ensures that the expression is parsed and returns an expected result.
    ValidResult(RcVar)
}

impl Assertion {
    /// Runs the assertion of a test case
    fn assert(&self, suite: &TestSuite, case: &TestCase) -> Result<(), String> {
        match self {
            &Assertion::InvalidArity => {
                match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
                    Err(RuntimeError::NotEnoughArguments{..}) => Ok(()),
                    Err(RuntimeError::TooManyArguments{..}) => Ok(()),
                    Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
                    Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
                }
            },
            &Assertion::InvalidType => {
                match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
                    Err(RuntimeError::InvalidType{..}) => Ok(()),
                    Err(RuntimeError::InvalidReturnType{..}) => Ok(()),
                    Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
                    Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
                }
            },
            &Assertion::InvalidSlice => {
                match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
                    Err(RuntimeError::InvalidSlice) => Ok(()),
                    Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
                    Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
                }
            },
            &Assertion::UnknownFunction => {
                match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
                    Err(RuntimeError::UnknownFunction{..}) => Ok(()),
                    Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
                    Ok(r) => Err(self.err_message(suite, case, r.to_string().unwrap())),
                }
            },
            &Assertion::SyntaxError => {
                match Expression::new(&case.expression) {
                    Err(_) => Ok(()),
                    Ok(expr) => Err(self.err_message(suite, case, format!("Parsed {:?}", expr)))
                }
            },
            &Assertion::ValidResult(ref expected_result) => {
                match try!(self.try_parse(suite, case)).search(suite.given.clone()) {
                    Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
                    Ok(r) => {
                        match r.as_string() {
                            Some(s) if s != expected_result.as_string().unwrap() => {
                                Err(self.err_message(suite, case, r.to_string().unwrap()))
                            },
                            Some(_) if r != expected_result.clone() => {
                                Err(self.err_message(suite, case, r.to_string().unwrap()))
                            },
                            _ => Ok(())
                        }
                    }
                }
            }
        }
    }

    /// Attempts to parse an expression for a case, returning the expression or an error string.
    fn try_parse<'a>(&self,
                     suite: &TestSuite,
                     case: &'a TestCase)
                     -> Result<Expression<'a>, String> {
        match Expression::new(&case.expression) {
            Err(e) => Err(self.err_message(suite, case, format!("{}", e))),
            Ok(expr) => Ok(expr)
        }
    }

    /// Formats an error message for a test case failure.
    fn err_message(&self, suite: &TestSuite, case: &TestCase, message: String) -> String {
        format!("Test suite: {}\nExpression: {}\nExpected: {}\nResult: {}\n==============",
                 suite.filename, case.expression, case.expected_result, message).to_string()
    }
}

struct TestSuite {
    /// Filename of the test suite
    filename: String,
    /// Given data of the test suite
    given: RcVar,
    /// Collection of test cases to perform
    cases: Vec<TestCase>,
}

impl TestSuite {
    /// Creates a test suite from parsed JSON data.
    fn from_variable(filename: String, suite: &RcVar) -> TestSuite {
        TestSuite {
            filename: filename,
            given: suite.get_value("given").unwrap(),
            cases: suite.get_value("cases").unwrap()
                .as_array()
                .unwrap()
                .iter()
                .map(|v| TestCase::from_variable(v))
                .collect::<Vec<TestCase>>()
        }
    }
}

struct TestCase {
    /// The expression being evaluated
    expression: String,
    /// The human-readable expected result of the test-case
    expected_result: String,
    /// The assertion to perform for the test case
    assertion: Assertion,
}

impl TestCase {
    /// Creates a test case from parsed JSON data.
    fn from_variable(case: &RcVar) -> TestCase {
        TestCase {
            expression: case.get_value("expression").unwrap().as_string().unwrap().clone(),
            expected_result: match case.get_value("error") {
                None => case.get_value("result").unwrap().to_string().unwrap(),
                Some(err) => err.as_string().unwrap().clone()
            },
            assertion: match case.get_value("error") {
                None => Assertion::ValidResult(case.get_value("result").unwrap()),
                Some(err) => {
                    match err.as_string().unwrap().as_ref() {
                        "syntax" => Assertion::SyntaxError,
                        "invalid-type" => Assertion::InvalidType,
                        "invalid-value" => Assertion::InvalidSlice,
                        "invalid-arity" => Assertion::InvalidArity,
                        "unknown-function" => Assertion::UnknownFunction,
                        v @ _ => panic!("Unknown error type: {}", v),
                    }
                }
            }
        }
    }
}

fn load_test_suites() -> Vec<TestSuite> {
    let mut result = vec![];
    let files = fs::read_dir("tests/compliance/").unwrap();
    for filename in files {
        let path = filename.unwrap().path();
        let file_path = path.to_str().unwrap().to_string();
        let mut f = File::open(path).unwrap();
        let mut file_data = String::new();
        f.read_to_string(&mut file_data).unwrap();
        let data = jmespath::Variable::from_str(&file_data).unwrap();
        let suites = data.as_array().unwrap();
        for suite in suites {
            result.push(TestSuite::from_variable(file_path.clone(), suite));
        }
    }
    result
}

#[test]
fn run_compliance() {
    let mut failures = 0;
    let suites = load_test_suites();
    for suite in suites.iter() {
        for case in suite.cases.iter() {
            if let Err(e) = case.assertion.assert(suite, case) {
                println!("{}\n", e);
                failures += 1;
            }
        }
    }
    if failures > 0 {
        panic!("{} compliance tests failed", failures);
    }
}
<|endoftext|>


<commit_before>use lapin::{
    message::DeliveryResult, options::*, publisher_confirm::Confirmation, types::FieldTable,
    BasicProperties, Connection, ConnectionProperties, ConsumerDelegate,
};
use log::info;
use std::{
    future::Future,
    pin::Pin,
    sync::{
        atomic::{AtomicU8, Ordering},
        Arc,
    },
    thread, time,
};

#[derive(Clone, Debug)]
struct Subscriber {
    hello_world: Arc<AtomicU8>,
}

impl ConsumerDelegate for Subscriber {
    fn on_new_delivery(
        &self,
        delivery: DeliveryResult,
    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        let subscriber = self.clone();
        Box::pin(async move {
            println!("received message: {:?}", delivery);

            if let Some((channel, delivery)) = delivery.unwrap() {
                println!("data: {}", std::str::from_utf8(&delivery.data).unwrap());

                channel
                    .basic_ack(delivery.delivery_tag, BasicAckOptions::default())
                    .await
                    .unwrap();

                assert_eq!(delivery.data, b"Hello world!");

                subscriber.hello_world.fetch_add(1, Ordering::SeqCst);
            }
        })
    }
}

#[test]
fn connection() {
    let _ = env_logger::try_init();

    let addr = std::env::var("AMQP_ADDR").unwrap_or_else(|_| "amqp://127.0.0.1:5672/%2f".into());
    let conn = Connection::connect(&addr, ConnectionProperties::default())
        .wait()
        .expect("connection error");

    println!("CONNECTED with configuration: {:?}", conn.configuration());

    //now connected

    //send channel
    let channel_a = conn.create_channel().wait().expect("create_channel");
    //receive channel
    let channel_b = conn.create_channel().wait().expect("create_channel");

    //create the hello queue
    let queue = channel_a
        .queue_declare(
            "hello-async",
            QueueDeclareOptions::default(),
            FieldTable::default(),
        )
        .wait()
        .expect("queue_declare");
    println!("[{}] state: {:?}", line!(), conn.status().state());
    println!("[{}] declared queue: {:?}", line!(), queue);

    //purge the hello queue in case it already exists with contents in it
    let queue = channel_a
        .queue_purge("hello-async", QueuePurgeOptions::default())
        .wait()
        .expect("queue_purge");
    println!("[{}] state: {:?}", line!(), conn.status().state());
    info!("Declared queue {:?}", queue);

    println!("will consume");
    let hello_world = Arc::new(AtomicU8::new(0));
    let subscriber = Subscriber {
        hello_world: hello_world.clone(),
    };
    let consumer = channel_b
        .basic_consume(
            "hello-async",
            "my_consumer",
            BasicConsumeOptions::default(),
            FieldTable::default(),
        )
        .wait()
        .expect("basic_consume");

    println!("will publish");
    let payload = b"Hello world!";
    let confirm = channel_a
        .basic_publish(
            "",
            "hello-async",
            BasicPublishOptions::default(),
            payload.to_vec(),
            BasicProperties::default(),
        )
        .wait()
        .expect("basic_publish")
        .wait()
        .expect("publisher-confirms");
    assert_eq!(confirm, Confirmation::NotRequested);

    consumer.set_delegate(subscriber).expect("set_delegate");
    println!("[{}] state: {:?}", line!(), conn.status().state());

    println!("will publish");
    let confirm = channel_a
        .basic_publish(
            "",
            "hello-async",
            BasicPublishOptions::default(),
            payload.to_vec(),
            BasicProperties::default(),
        )
        .wait()
        .expect("basic_publish")
        .wait()
        .expect("publisher-confirms");
    assert_eq!(confirm, Confirmation::NotRequested);
    println!("[{}] state: {:?}", line!(), conn.status().state());

    thread::sleep(time::Duration::from_millis(100));
    assert_eq!(hello_world.load(Ordering::SeqCst), 2);
}
<commit_msg>make the test use async-await<commit_after>use futures_executor::LocalPool;
use lapin::{
    message::DeliveryResult, options::*, publisher_confirm::Confirmation, types::FieldTable,
    BasicProperties, Connection, ConnectionProperties, ConsumerDelegate,
};
use log::info;
use std::{
    future::Future,
    pin::Pin,
    sync::{
        atomic::{AtomicU8, Ordering},
        Arc,
    },
    thread, time,
};

#[derive(Clone, Debug)]
struct Subscriber {
    hello_world: Arc<AtomicU8>,
}

impl ConsumerDelegate for Subscriber {
    fn on_new_delivery(
        &self,
        delivery: DeliveryResult,
    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        let subscriber = self.clone();
        Box::pin(async move {
            println!("received message: {:?}", delivery);

            if let Some((channel, delivery)) = delivery.unwrap() {
                println!("data: {}", std::str::from_utf8(&delivery.data).unwrap());

                assert_eq!(delivery.data, b"Hello world!");

                subscriber.hello_world.fetch_add(1, Ordering::SeqCst);

                channel
                    .basic_ack(delivery.delivery_tag, BasicAckOptions::default())
                    .await
                    .unwrap();
            }
        })
    }
}

#[test]
fn connection() {
    let _ = env_logger::try_init();

    let addr = std::env::var("AMQP_ADDR").unwrap_or_else(|_| "amqp://127.0.0.1:5672/%2f".into());

    LocalPool::new().run_until(async {
        let conn = Connection::connect(&addr, ConnectionProperties::default())
            .await
            .expect("connection error");

        println!("CONNECTED with configuration: {:?}", conn.configuration());

        //now connected

        //send channel
        let channel_a = conn.create_channel().await.expect("create_channel");
        //receive channel
        let channel_b = conn.create_channel().await.expect("create_channel");

        channel_a.confirm_select(ConfirmSelectOptions::default()).await.expect("confirm_select");

        //create the hello queue
        let queue = channel_a
            .queue_declare(
                "hello-async",
                QueueDeclareOptions::default(),
                FieldTable::default(),
            )
            .await
            .expect("queue_declare");
        println!("[{}] state: {:?}", line!(), conn.status().state());
        println!("[{}] declared queue: {:?}", line!(), queue);

        //purge the hello queue in case it already exists with contents in it
        let queue = channel_a
            .queue_purge("hello-async", QueuePurgeOptions::default())
            .await
            .expect("queue_purge");
        println!("[{}] state: {:?}", line!(), conn.status().state());
        info!("Declared queue {:?}", queue);

        println!("will consume");
        let hello_world = Arc::new(AtomicU8::new(0));
        let subscriber = Subscriber {
            hello_world: hello_world.clone(),
        };
        let consumer = channel_b
            .basic_consume(
                "hello-async",
                "my_consumer",
                BasicConsumeOptions::default(),
                FieldTable::default(),
            )
            .await
            .expect("basic_consume");

        println!("will publish");
        let payload = b"Hello world!";
        let confirm = channel_a
            .basic_publish(
                "",
                "hello-async",
                BasicPublishOptions::default(),
                payload.to_vec(),
                BasicProperties::default(),
            )
            .await
            .expect("basic_publish")
            .await
            .expect("publisher-confirms");
        assert_eq!(confirm, Confirmation::Ack(None));

        consumer.set_delegate(subscriber).expect("set_delegate");
        println!("[{}] state: {:?}", line!(), conn.status().state());

        println!("will publish");
        let confirm = channel_a
            .basic_publish(
                "",
                "hello-async",
                BasicPublishOptions::default(),
                payload.to_vec(),
                BasicProperties::default(),
            )
            .await
            .expect("basic_publish")
            .await
            .expect("publisher-confirms");
        assert_eq!(confirm, Confirmation::Ack(None));
        println!("[{}] state: {:?}", line!(), conn.status().state());

        thread::sleep(time::Duration::from_millis(500));
        assert_eq!(hello_world.load(Ordering::SeqCst), 2);
    });
}
<|endoftext|>


<commit_before>use byteorder::{LittleEndian, WriteBytesExt};
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::{Cursor, Seek};
use std::iter::FromIterator;
use zip::write::FileOptions;
use zip::{CompressionMethod, SUPPORTED_METHODS};

// This test asserts that after creating a zip file, then reading its contents back out,
// the extracted data will *always* be exactly the same as the original data.
#[test]
fn end_to_end() {
    for &method in SUPPORTED_METHODS {
        let file = &mut Cursor::new(Vec::new());

        println!("Writing file with {} compression", method);
        write_test_archive(file, method).expect("Couldn't write test zip archive");

        println!("Checking file contents");
        check_archive_file(file, ENTRY_NAME, Some(method), LOREM_IPSUM);
    }
}

// This test asserts that after copying a `ZipFile` to a new `ZipWriter`, then reading its
// contents back out, the extracted data will *always* be exactly the same as the original data.
#[test]
fn copy() {
    for &method in SUPPORTED_METHODS {
        let src_file = &mut Cursor::new(Vec::new());
        write_test_archive(src_file, method).expect("Couldn't write to test file");

        let mut tgt_file = &mut Cursor::new(Vec::new());

        {
            let mut src_archive = zip::ZipArchive::new(src_file).unwrap();
            let mut zip = zip::ZipWriter::new(&mut tgt_file);

            {
                let file = src_archive
                    .by_name(ENTRY_NAME)
                    .expect("Missing expected file");

                zip.raw_copy_file(file).expect("Couldn't copy file");
            }

            {
                let file = src_archive
                    .by_name(ENTRY_NAME)
                    .expect("Missing expected file");

                zip.raw_copy_file_rename(file, COPY_ENTRY_NAME)
                    .expect("Couldn't copy and rename file");
            }
        }

        let mut tgt_archive = zip::ZipArchive::new(tgt_file).unwrap();

        check_archive_file_contents(&mut tgt_archive, ENTRY_NAME, LOREM_IPSUM);
        check_archive_file_contents(&mut tgt_archive, COPY_ENTRY_NAME, LOREM_IPSUM);
    }
}

// This test asserts that after appending to a `ZipWriter`, then reading its contents back out,
// both the prior data and the appended data will be exactly the same as their originals.
#[test]
fn append() {
    for &method in SUPPORTED_METHODS {
        let mut file = &mut Cursor::new(Vec::new());
        write_test_archive(file, method).expect("Couldn't write to test file");

        {
            let mut zip = zip::ZipWriter::new_append(&mut file).unwrap();
            zip.start_file(
                COPY_ENTRY_NAME,
                FileOptions::default().compression_method(method),
            )
            .unwrap();
            zip.write_all(LOREM_IPSUM).unwrap();
            zip.finish().unwrap();
        }

        let mut zip = zip::ZipArchive::new(&mut file).unwrap();
        check_archive_file_contents(&mut zip, ENTRY_NAME, LOREM_IPSUM);
        check_archive_file_contents(&mut zip, COPY_ENTRY_NAME, LOREM_IPSUM);
    }
}

// Write a test zip archive to buffer.
fn write_test_archive(
    file: &mut Cursor<Vec<u8>>,
    method: CompressionMethod,
) -> zip::result::ZipResult<()> {
    let mut zip = zip::ZipWriter::new(file);

    zip.add_directory("test/", Default::default())?;

    let options = FileOptions::default()
        .compression_method(method)
        .unix_permissions(0o755);

    zip.start_file("test/☃.txt", options)?;
    zip.write_all(b"Hello, World!\n")?;

    zip.start_file_with_extra_data("test_with_extra_data/🐢.txt", options)?;
    zip.write_u16::<LittleEndian>(0xbeef)?;
    zip.write_u16::<LittleEndian>(EXTRA_DATA.len() as u16)?;
    zip.write_all(EXTRA_DATA)?;
    zip.end_extra_data()?;
    zip.write_all(b"Hello, World! Again.\n")?;

    zip.start_file(ENTRY_NAME, options)?;
    zip.write_all(LOREM_IPSUM)?;

    zip.finish()?;
    Ok(())
}

// Load an archive from buffer and check for test data.
fn check_test_archive<R: Read + Seek>(zip_file: R) -> zip::result::ZipResult<zip::ZipArchive<R>> {
    let mut archive = zip::ZipArchive::new(zip_file).unwrap();

    // Check archive contains expected file names.
    {
        let expected_file_names = [
            "test/",
            "test/☃.txt",
            "test_with_extra_data/🐢.txt",
            ENTRY_NAME,
        ];
        let expected_file_names = HashSet::from_iter(expected_file_names.iter().copied());
        let file_names = archive.file_names().collect::<HashSet<_>>();
        assert_eq!(file_names, expected_file_names);
    }

    // Check an archive file for extra data field contents.
    {
        let file_with_extra_data = archive.by_name("test_with_extra_data/🐢.txt")?;
        let mut extra_data = Vec::new();
        extra_data.write_u16::<LittleEndian>(0xbeef)?;
        extra_data.write_u16::<LittleEndian>(EXTRA_DATA.len() as u16)?;
        extra_data.write_all(EXTRA_DATA)?;
        assert_eq!(file_with_extra_data.extra_data(), extra_data.as_slice());
    }

    Ok(archive)
}

// Read a file in the archive as a string.
fn read_archive_file<R: Read + Seek>(
    archive: &mut zip::ZipArchive<R>,
    name: &str,
) -> zip::result::ZipResult<String> {
    let mut file = archive.by_name(name)?;

    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();

    Ok(contents)
}

// Check a file in the archive contains expected data and properties.
fn check_archive_file(
    zip_file: &mut Cursor<Vec<u8>>,
    name: &str,
    expected_method: Option<CompressionMethod>,
    expected_data: &[u8],
) {
    let mut archive = check_test_archive(zip_file).unwrap();

    if let Some(expected_method) = expected_method {
        // Check the file's compression method.
        let file = archive.by_name(name).unwrap();
        let real_method = file.compression();

        assert_eq!(
            expected_method, real_method,
            "File does not have expected compression method"
        );
    }

    check_archive_file_contents(&mut archive, name, expected_data);
}

// Check a file in the archive contains the given data.
fn check_archive_file_contents<R: Read + Seek>(
    archive: &mut zip::ZipArchive<R>,
    name: &str,
    expected: &[u8],
) {
    let file_contents: String = read_archive_file(archive, name).unwrap();
    assert_eq!(file_contents.as_bytes(), expected);
}

const LOREM_IPSUM : &[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tellus elit, tristique vitae mattis egestas, ultricies vitae risus. Quisque sit amet quam ut urna aliquet
molestie. Proin blandit ornare dui, a tempor nisl accumsan in. Praesent a consequat felis. Morbi metus diam, auctor in auctor vel, feugiat id odio. Curabitur ex ex,
dictum quis auctor quis, suscipit id lorem. Aliquam vestibulum dolor nec enim vehicula, porta tristique augue tincidunt. Vivamus ut gravida est. Sed pellentesque, dolor
vitae tristique consectetur, neque lectus pulvinar dui, sed feugiat purus diam id lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos. Maecenas feugiat velit in ex ultrices scelerisque id id neque.
";

const EXTRA_DATA: &[u8] = b"Extra Data";

const ENTRY_NAME: &str = "test/lorem_ipsum.txt";

const COPY_ENTRY_NAME: &str = "test/lorem_ipsum_renamed.txt";
<commit_msg>Update end to end methods import<commit_after>use byteorder::{LittleEndian, WriteBytesExt};
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::{Cursor, Seek};
use std::iter::FromIterator;
use zip::write::FileOptions;
use zip::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};

// This test asserts that after creating a zip file, then reading its contents back out,
// the extracted data will *always* be exactly the same as the original data.
#[test]
fn end_to_end() {
    for &method in SUPPORTED_COMPRESSION_METHODS {
        let file = &mut Cursor::new(Vec::new());

        println!("Writing file with {} compression", method);
        write_test_archive(file, method).expect("Couldn't write test zip archive");

        println!("Checking file contents");
        check_archive_file(file, ENTRY_NAME, Some(method), LOREM_IPSUM);
    }
}

// This test asserts that after copying a `ZipFile` to a new `ZipWriter`, then reading its
// contents back out, the extracted data will *always* be exactly the same as the original data.
#[test]
fn copy() {
    for &method in SUPPORTED_COMPRESSION_METHODS {
        let src_file = &mut Cursor::new(Vec::new());
        write_test_archive(src_file, method).expect("Couldn't write to test file");

        let mut tgt_file = &mut Cursor::new(Vec::new());

        {
            let mut src_archive = zip::ZipArchive::new(src_file).unwrap();
            let mut zip = zip::ZipWriter::new(&mut tgt_file);

            {
                let file = src_archive
                    .by_name(ENTRY_NAME)
                    .expect("Missing expected file");

                zip.raw_copy_file(file).expect("Couldn't copy file");
            }

            {
                let file = src_archive
                    .by_name(ENTRY_NAME)
                    .expect("Missing expected file");

                zip.raw_copy_file_rename(file, COPY_ENTRY_NAME)
                    .expect("Couldn't copy and rename file");
            }
        }

        let mut tgt_archive = zip::ZipArchive::new(tgt_file).unwrap();

        check_archive_file_contents(&mut tgt_archive, ENTRY_NAME, LOREM_IPSUM);
        check_archive_file_contents(&mut tgt_archive, COPY_ENTRY_NAME, LOREM_IPSUM);
    }
}

// This test asserts that after appending to a `ZipWriter`, then reading its contents back out,
// both the prior data and the appended data will be exactly the same as their originals.
#[test]
fn append() {
    for &method in SUPPORTED_COMPRESSION_METHODS {
        let mut file = &mut Cursor::new(Vec::new());
        write_test_archive(file, method).expect("Couldn't write to test file");

        {
            let mut zip = zip::ZipWriter::new_append(&mut file).unwrap();
            zip.start_file(
                COPY_ENTRY_NAME,
                FileOptions::default().compression_method(method),
            )
            .unwrap();
            zip.write_all(LOREM_IPSUM).unwrap();
            zip.finish().unwrap();
        }

        let mut zip = zip::ZipArchive::new(&mut file).unwrap();
        check_archive_file_contents(&mut zip, ENTRY_NAME, LOREM_IPSUM);
        check_archive_file_contents(&mut zip, COPY_ENTRY_NAME, LOREM_IPSUM);
    }
}

// Write a test zip archive to buffer.
fn write_test_archive(
    file: &mut Cursor<Vec<u8>>,
    method: CompressionMethod,
) -> zip::result::ZipResult<()> {
    let mut zip = zip::ZipWriter::new(file);

    zip.add_directory("test/", Default::default())?;

    let options = FileOptions::default()
        .compression_method(method)
        .unix_permissions(0o755);

    zip.start_file("test/☃.txt", options)?;
    zip.write_all(b"Hello, World!\n")?;

    zip.start_file_with_extra_data("test_with_extra_data/🐢.txt", options)?;
    zip.write_u16::<LittleEndian>(0xbeef)?;
    zip.write_u16::<LittleEndian>(EXTRA_DATA.len() as u16)?;
    zip.write_all(EXTRA_DATA)?;
    zip.end_extra_data()?;
    zip.write_all(b"Hello, World! Again.\n")?;

    zip.start_file(ENTRY_NAME, options)?;
    zip.write_all(LOREM_IPSUM)?;

    zip.finish()?;
    Ok(())
}

// Load an archive from buffer and check for test data.
fn check_test_archive<R: Read + Seek>(zip_file: R) -> zip::result::ZipResult<zip::ZipArchive<R>> {
    let mut archive = zip::ZipArchive::new(zip_file).unwrap();

    // Check archive contains expected file names.
    {
        let expected_file_names = [
            "test/",
            "test/☃.txt",
            "test_with_extra_data/🐢.txt",
            ENTRY_NAME,
        ];
        let expected_file_names = HashSet::from_iter(expected_file_names.iter().copied());
        let file_names = archive.file_names().collect::<HashSet<_>>();
        assert_eq!(file_names, expected_file_names);
    }

    // Check an archive file for extra data field contents.
    {
        let file_with_extra_data = archive.by_name("test_with_extra_data/🐢.txt")?;
        let mut extra_data = Vec::new();
        extra_data.write_u16::<LittleEndian>(0xbeef)?;
        extra_data.write_u16::<LittleEndian>(EXTRA_DATA.len() as u16)?;
        extra_data.write_all(EXTRA_DATA)?;
        assert_eq!(file_with_extra_data.extra_data(), extra_data.as_slice());
    }

    Ok(archive)
}

// Read a file in the archive as a string.
fn read_archive_file<R: Read + Seek>(
    archive: &mut zip::ZipArchive<R>,
    name: &str,
) -> zip::result::ZipResult<String> {
    let mut file = archive.by_name(name)?;

    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();

    Ok(contents)
}

// Check a file in the archive contains expected data and properties.
fn check_archive_file(
    zip_file: &mut Cursor<Vec<u8>>,
    name: &str,
    expected_method: Option<CompressionMethod>,
    expected_data: &[u8],
) {
    let mut archive = check_test_archive(zip_file).unwrap();

    if let Some(expected_method) = expected_method {
        // Check the file's compression method.
        let file = archive.by_name(name).unwrap();
        let real_method = file.compression();

        assert_eq!(
            expected_method, real_method,
            "File does not have expected compression method"
        );
    }

    check_archive_file_contents(&mut archive, name, expected_data);
}

// Check a file in the archive contains the given data.
fn check_archive_file_contents<R: Read + Seek>(
    archive: &mut zip::ZipArchive<R>,
    name: &str,
    expected: &[u8],
) {
    let file_contents: String = read_archive_file(archive, name).unwrap();
    assert_eq!(file_contents.as_bytes(), expected);
}

const LOREM_IPSUM : &[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tellus elit, tristique vitae mattis egestas, ultricies vitae risus. Quisque sit amet quam ut urna aliquet
molestie. Proin blandit ornare dui, a tempor nisl accumsan in. Praesent a consequat felis. Morbi metus diam, auctor in auctor vel, feugiat id odio. Curabitur ex ex,
dictum quis auctor quis, suscipit id lorem. Aliquam vestibulum dolor nec enim vehicula, porta tristique augue tincidunt. Vivamus ut gravida est. Sed pellentesque, dolor
vitae tristique consectetur, neque lectus pulvinar dui, sed feugiat purus diam id lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos. Maecenas feugiat velit in ex ultrices scelerisque id id neque.
";

const EXTRA_DATA: &[u8] = b"Extra Data";

const ENTRY_NAME: &str = "test/lorem_ipsum.txt";

const COPY_ENTRY_NAME: &str = "test/lorem_ipsum_renamed.txt";
<|endoftext|>