<commit_before>use crate::support::{database, project};
use diesel::dsl::sql;
use diesel::sql_types::Bool;
use diesel::{select, RunQueryDsl};
use std::path::Path;

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

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

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

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

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

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

    let migrations_done: bool = select(sql::<Bool>(
        "EXISTS (SELECT * FROM __diesel_schema_migrations WHERE version >= '1')",
    ))
    .get_result(&db.conn())
    .unwrap();
    assert!(!migrations_done, "Migrations table should be empty");

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

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

    // By running a query that compares timestamps, we are also checking
    // that the auto-inserted values for the "run_on" column are valid.

    #[cfg(feature = "sqlite")]
    fn valid_run_on_timestamp(db: &database::Database) -> bool {
        select(sql::<Bool>(
            "EXISTS (SELECT 1 FROM __diesel_schema_migrations \
             WHERE run_on < DATETIME('now', '+1 hour'))",
        ))
        .get_result(&db.conn())
        .unwrap()
    }

    #[cfg(feature = "postgres")]
    fn valid_run_on_timestamp(db: &database::Database) -> bool {
        select(sql::<Bool>(
            "EXISTS (SELECT 1 FROM __diesel_schema_migrations \
             WHERE run_on < NOW() + INTERVAL '1 hour')",
        ))
        .get_result(&db.conn())
        .unwrap()
    }

    #[cfg(feature = "mysql")]
    fn valid_run_on_timestamp(db: &database::Database) -> bool {
        select(sql::<Bool>(
            "EXISTS (SELECT 1 FROM __diesel_schema_migrations \
             WHERE run_on < NOW() + INTERVAL 1 HOUR)",
        ))
        .get_result(&db.conn())
        .unwrap()
    }

    assert!(
        valid_run_on_timestamp(&db),
        "Running a migration did not insert an updated run_on value"
    );
}

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

    p.command("setup").run();

    p.create_migration("12345_empty_migration", "", "");

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

    assert!(!result.is_success());
    assert!(result.stderr().contains(
        "Failed to run 12345_empty_migration with: Attempted to run an empty migration."
    ));
}

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

    p.command("setup").run();

    p.create_migration(
        "run_error_migrations_fails",
        "CREATE TABLE users (id INTEGER PRIMARY KEY}",
        "DROP TABLE users",
    );

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

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

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

    p.command("setup").run();

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

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

    assert!(result.stdout().contains("true\n"));
}

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

    p.command("setup").run();

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

    p.command("migration").arg("run").run();

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

    assert!(result.stdout().contains("false\n"));
}

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

    p.command("setup").run();

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

    p.command("migration").arg("run").run();

    p.create_migration(
        "123456_create_posts_table",
        "CREATE TABLE posts (id INTEGER PRIMARY KEY)",
        "DROP TABLE posts",
    );

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

    assert!(result.stdout().contains("true\n"));
}

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

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

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

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

    let result = p
        .command_without_database_url("migration")
        .arg("run")
        .arg("--database-url")
        .arg(db_url)
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

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

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

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

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

    let result = p
        .command_without_database_url("migration")
        .arg("--database-url")
        .arg(db_url)
        .arg("run")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn migration_run_runs_pending_migrations_custom_migration_dir_1() {
    let p = project("migration_run_custom_migration_dir_1")
        .folder("custom_migrations")
        .build();
    let db_url = p.database_url();
    let db = database(&db_url);

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

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

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

    let result = p
        .command("migration")
        .arg("run")
        .arg("--migration-dir")
        .arg(p.migration_dir_in_directory("custom_migrations"))
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn migration_run_runs_pending_migrations_custom_migration_dir_2() {
    let p = project("migration_run_custom_migration_dir_2")
        .folder("custom_migrations")
        .build();
    let db_url = p.database_url();
    let db = database(&db_url);

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

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

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

    let result = p
        .command("migration")
        .arg("--migration-dir")
        .arg(p.migration_dir_in_directory("custom_migrations"))
        .arg("run")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn migration_run_updates_schema_if_config_present() {
    let p = project("migration_run_updates_schema_if_config_present")
        .folder("migrations")
        .file(
            "diesel.toml",
            r#"
            [print_schema]
            file = "src/my_schema.rs"
            "#,
        )
        .build();

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

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

    assert!(!p.has_file("src/my_schema.rs"));

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(p.has_file("src/my_schema.rs"));
}

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

    p.command("database").arg("setup").run();

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

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

    let cargo_toml_path = Path::new("Cargo.toml");
    p.delete_single_file(&cargo_toml_path);

    p.command("database").arg("setup").run();

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn verify_schema_errors_if_schema_file_would_change() {
    let p = project("migration_run_verify_schema_errors")
        .folder("migrations")
        .file(
            "diesel.toml",
            r#"
            [print_schema]
            file = "src/my_schema.rs"
            "#,
        )
        .build();

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

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

    assert!(!p.has_file("src/my_schema.rs"));

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(p.has_file("src/my_schema.rs"));

    p.create_migration(
        "12346_create_posts_table",
        "CREATE TABLE posts (id INTEGER PRIMARY KEY)",
        "DROP TABLE posts",
    );

    let result = p
        .command("migration")
        .arg("run")
        .arg("--locked-schema")
        .run();

    assert!(
        !result.is_success(),
        "Result was successful, expected to fail {:?}",
        result
    );
    assert!(
        result
            .stderr()
            .contains("Command would result in changes to")
            && result.stderr().contains("src/my_schema.rs"),
        "Unexpected stderr {}",
        result.stderr()
    );
    assert!(p.has_file("src/my_schema.rs"));
}

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

    let db_url = p.database_url();
    let db = database(&db_url);

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

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}
<commit_msg>test: add test<commit_after>use crate::support::{database, project};
use diesel::dsl::sql;
use diesel::sql_types::Bool;
use diesel::{select, RunQueryDsl};
use std::path::Path;

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

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

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

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

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

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

    let migrations_done: bool = select(sql::<Bool>(
        "EXISTS (SELECT * FROM __diesel_schema_migrations WHERE version >= '1')",
    ))
    .get_result(&db.conn())
    .unwrap();
    assert!(!migrations_done, "Migrations table should be empty");

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

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

    // By running a query that compares timestamps, we are also checking
    // that the auto-inserted values for the "run_on" column are valid.

    #[cfg(feature = "sqlite")]
    fn valid_run_on_timestamp(db: &database::Database) -> bool {
        select(sql::<Bool>(
            "EXISTS (SELECT 1 FROM __diesel_schema_migrations \
             WHERE run_on < DATETIME('now', '+1 hour'))",
        ))
        .get_result(&db.conn())
        .unwrap()
    }

    #[cfg(feature = "postgres")]
    fn valid_run_on_timestamp(db: &database::Database) -> bool {
        select(sql::<Bool>(
            "EXISTS (SELECT 1 FROM __diesel_schema_migrations \
             WHERE run_on < NOW() + INTERVAL '1 hour')",
        ))
        .get_result(&db.conn())
        .unwrap()
    }

    #[cfg(feature = "mysql")]
    fn valid_run_on_timestamp(db: &database::Database) -> bool {
        select(sql::<Bool>(
            "EXISTS (SELECT 1 FROM __diesel_schema_migrations \
             WHERE run_on < NOW() + INTERVAL 1 HOUR)",
        ))
        .get_result(&db.conn())
        .unwrap()
    }

    assert!(
        valid_run_on_timestamp(&db),
        "Running a migration did not insert an updated run_on value"
    );
}

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

    p.command("setup").run();

    p.create_migration("12345_empty_migration", "", "");

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

    assert!(!result.is_success());
    assert!(result.stderr().contains(
        "Failed to run 12345_empty_migration with: Attempted to run an empty migration."
    ));
}

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

    p.command("setup").run();

    p.create_migration(
        "run_error_migrations_fails",
        "CREATE TABLE users (id INTEGER PRIMARY KEY}",
        "DROP TABLE users",
    );

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

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

#[test]
#[cfg(feature = "postgres")]
fn error_migrations_when_use_invalid_database_url() {
    let p = project("error_migrations_when_use_invalid_database_url")
        .folder("migrations")
        .build();

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

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

    let result = p
        .command_without_database_url("migration")
        .arg("run")
        .arg("--database-url")
        .arg("postgres://localhost/lemmy")
        .run();

    assert!(!result.is_success());
    assert!(result
        .stderr()
        .contains("Could not connect to database via `postgres://localhost/lemmy`:"));
}

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

    p.command("setup").run();

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

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

    assert!(result.stdout().contains("true\n"));
}

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

    p.command("setup").run();

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

    p.command("migration").arg("run").run();

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

    assert!(result.stdout().contains("false\n"));
}

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

    p.command("setup").run();

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

    p.command("migration").arg("run").run();

    p.create_migration(
        "123456_create_posts_table",
        "CREATE TABLE posts (id INTEGER PRIMARY KEY)",
        "DROP TABLE posts",
    );

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

    assert!(result.stdout().contains("true\n"));
}

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

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

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

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

    let result = p
        .command_without_database_url("migration")
        .arg("run")
        .arg("--database-url")
        .arg(db_url)
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

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

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

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

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

    let result = p
        .command_without_database_url("migration")
        .arg("--database-url")
        .arg(db_url)
        .arg("run")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn migration_run_runs_pending_migrations_custom_migration_dir_1() {
    let p = project("migration_run_custom_migration_dir_1")
        .folder("custom_migrations")
        .build();
    let db_url = p.database_url();
    let db = database(&db_url);

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

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

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

    let result = p
        .command("migration")
        .arg("run")
        .arg("--migration-dir")
        .arg(p.migration_dir_in_directory("custom_migrations"))
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn migration_run_runs_pending_migrations_custom_migration_dir_2() {
    let p = project("migration_run_custom_migration_dir_2")
        .folder("custom_migrations")
        .build();
    let db_url = p.database_url();
    let db = database(&db_url);

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

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

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

    let result = p
        .command("migration")
        .arg("--migration-dir")
        .arg(p.migration_dir_in_directory("custom_migrations"))
        .arg("run")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn migration_run_updates_schema_if_config_present() {
    let p = project("migration_run_updates_schema_if_config_present")
        .folder("migrations")
        .file(
            "diesel.toml",
            r#"
            [print_schema]
            file = "src/my_schema.rs"
            "#,
        )
        .build();

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

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

    assert!(!p.has_file("src/my_schema.rs"));

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(p.has_file("src/my_schema.rs"));
}

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

    p.command("database").arg("setup").run();

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

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

    let cargo_toml_path = Path::new("Cargo.toml");
    p.delete_single_file(&cargo_toml_path);

    p.command("database").arg("setup").run();

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}

#[test]
fn verify_schema_errors_if_schema_file_would_change() {
    let p = project("migration_run_verify_schema_errors")
        .folder("migrations")
        .file(
            "diesel.toml",
            r#"
            [print_schema]
            file = "src/my_schema.rs"
            "#,
        )
        .build();

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

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

    assert!(!p.has_file("src/my_schema.rs"));

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(p.has_file("src/my_schema.rs"));

    p.create_migration(
        "12346_create_posts_table",
        "CREATE TABLE posts (id INTEGER PRIMARY KEY)",
        "DROP TABLE posts",
    );

    let result = p
        .command("migration")
        .arg("run")
        .arg("--locked-schema")
        .run();

    assert!(
        !result.is_success(),
        "Result was successful, expected to fail {:?}",
        result
    );
    assert!(
        result
            .stderr()
            .contains("Command would result in changes to")
            && result.stderr().contains("src/my_schema.rs"),
        "Unexpected stderr {}",
        result.stderr()
    );
    assert!(p.has_file("src/my_schema.rs"));
}

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

    let db_url = p.database_url();
    let db = database(&db_url);

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

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

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

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

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Running migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(db.table_exists("users"));
}
<|endoftext|>


<commit_before>#![crate_name = "helloworld-server"]

/// Hello World server in Rust
/// Binds REP socket to tcp://*:5555
/// Expects "Hello" from client, replies with "World"

extern crate zmq;

use std::old_io;
use std::time::Duration;

fn main() {
    let mut context = zmq::Context::new();
    let mut responder = context.socket(zmq::REP).unwrap();

    assert!(responder.bind("tcp://*:5555").is_ok());

    let mut msg = zmq::Message::new().unwrap();
    loop {
        responder.recv(&mut msg, 0).unwrap();
        println!("Received {}", msg.as_str().unwrap());
        responder.send_str("World", 0).unwrap();
        old_io::timer::sleep(Duration::seconds(1));
    }
}
<commit_msg>old_io is still needed for sleeping<commit_after>#![crate_name = "helloworld-server"]
#![feature(old_io, std_misc)]

/// Hello World server in Rust
/// Binds REP socket to tcp://*:5555
/// Expects "Hello" from client, replies with "World"

extern crate zmq;

use std::old_io;
use std::time::Duration;

fn main() {
    let mut context = zmq::Context::new();
    let mut responder = context.socket(zmq::REP).unwrap();

    assert!(responder.bind("tcp://*:5555").is_ok());

    let mut msg = zmq::Message::new().unwrap();
    loop {
        responder.recv(&mut msg, 0).unwrap();
        println!("Received {}", msg.as_str().unwrap());
        responder.send_str("World", 0).unwrap();
        old_io::timer::sleep(Duration::seconds(1));
    }
}
<|endoftext|>


<commit_before>use std::str;

extern crate sysfs_gpio;
extern crate clerk;

use clerk::{SeekFrom, Display, DisplayPins, DisplayHardwareLayer, DefaultLines, DisplayState,
            CursorState, CursorBlinking, Direction};

struct ExternPin(sysfs_gpio::Pin);

impl From<u64> for ExternPin {
    fn from(i: u64) -> Self {
        ExternPin(sysfs_gpio::Pin::new(i))
    }
}

impl DisplayHardwareLayer for ExternPin {
    fn init(&self) {
        self.0.export().unwrap();
        self.0.set_direction(sysfs_gpio::Direction::Out).unwrap();
    }

    fn cleanup(&self) {
        self.0.unexport().unwrap();
    }

    fn set_direction(&self, direction: Direction) {
        let native_direction = match direction {
            Direction::In => sysfs_gpio::Direction::In,
            Direction::Out => sysfs_gpio::Direction::Out,
        };

        self.0.set_direction(native_direction).unwrap();
    }

    fn set_value(&self, value: u8) -> Result<(), ()> {
        self.0.set_value(value).map_err(|_| ())
    }

    fn get_value(&self) -> u8 {
        self.0.get_value().unwrap()
    }
}

fn main() {
    let pins = DisplayPins {
        register_select: 2,
        read: 3,
        enable: 4,
        data4: 16,
        data5: 19,
        data6: 26,
        data7: 20,
    };

    let mut lcd: Display<ExternPin, DefaultLines> = Display::from_pins(pins);

    lcd.set_display_control(|e| {
        e.set_display(DisplayState::On)
            .set_cursor(CursorState::Off)
            .set_cursor_blinking(CursorBlinking::On);
    });

    lcd.seek_cgram(SeekFrom::Home(0));
    let character = [
        0b01110,
        0b10101,
        0b11111,
        0b10101,
        0b01110,
        0b00100,
        0b00100,
        0b11111,
    ];
    lcd.write_message(str::from_utf8(&character).unwrap());

    let msg = [0];
    lcd.seek(SeekFrom::Home(0));
    lcd.write_message(str::from_utf8(&msg).unwrap());
}
<commit_msg>Add example for reading from character generator RAM<commit_after>use std::str;

extern crate sysfs_gpio;
extern crate clerk;

use clerk::{SeekFrom, Display, DisplayPins, DisplayHardwareLayer, DefaultLines, DisplayState,
            CursorState, CursorBlinking, Direction};

struct ExternPin(sysfs_gpio::Pin);

impl From<u64> for ExternPin {
    fn from(i: u64) -> Self {
        ExternPin(sysfs_gpio::Pin::new(i))
    }
}

impl DisplayHardwareLayer for ExternPin {
    fn init(&self) {
        self.0.export().unwrap();
        self.0.set_direction(sysfs_gpio::Direction::Out).unwrap();
    }

    fn cleanup(&self) {
        self.0.unexport().unwrap();
    }

    fn set_direction(&self, direction: Direction) {
        let native_direction = match direction {
            Direction::In => sysfs_gpio::Direction::In,
            Direction::Out => sysfs_gpio::Direction::Out,
        };

        self.0.set_direction(native_direction).unwrap();
    }

    fn set_value(&self, value: u8) -> Result<(), ()> {
        self.0.set_value(value).map_err(|_| ())
    }

    fn get_value(&self) -> u8 {
        self.0.get_value().unwrap()
    }
}

fn main() {
    let pins = DisplayPins {
        register_select: 2,
        read: 3,
        enable: 4,
        data4: 16,
        data5: 19,
        data6: 26,
        data7: 20,
    };

    let mut lcd: Display<ExternPin, DefaultLines> = Display::from_pins(pins);

    lcd.set_display_control(|e| {
        e.set_display(DisplayState::On)
            .set_cursor(CursorState::Off)
            .set_cursor_blinking(CursorBlinking::On);
    });

    lcd.seek_cgram(SeekFrom::Home(0));
    let character = [
        0b01110,
        0b10101,
        0b11111,
        0b10101,
        0b01110,
        0b00100,
        0b00100,
        0b11111,
    ];
    lcd.write_message(str::from_utf8(&character).unwrap());

    lcd.seek_cgram(SeekFrom::Home(0));
    println!("Created custom char is: ");
    for _ in 0..8 {
        let value = lcd.read_byte();
        println!("{:#08b}", value)
    }

    let msg = [0];
    lcd.seek(SeekFrom::Home(0));
    lcd.write_message(str::from_utf8(&msg).unwrap());
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2014 Y. T. CHUNG

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use relay::loadbalancing::server::LoadBalancer;
use config::{Config, MultipleServer, SingleServer, ServerConfig, ServerConfigVariant};

#[deriving(Clone)]
pub struct RoundRobin {
    server: ServerConfigVariant,
    index: uint,
}

impl RoundRobin {
    pub fn new(config: ServerConfigVariant) -> RoundRobin {
        RoundRobin {
            server: config,
            index: 0u,
        }
    }
}

impl LoadBalancer for RoundRobin {
    fn pick_server<'a>(&'a mut self) -> &'a ServerConfig {
        match self.server {
            SingleServer(ref s) => s,
            MultipleServer(ref slist) => {
                let ref s = slist[self.index];
                self.index = (self.index + 1) % slist.len();
                s
            }
        }
    }
}
<commit_msg>load balancer just need the server config<commit_after>// The MIT License (MIT)

// Copyright (c) 2014 Y. T. CHUNG

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use relay::loadbalancing::server::LoadBalancer;
use config::{MultipleServer, SingleServer, ServerConfig, ServerConfigVariant};

#[deriving(Clone)]
pub struct RoundRobin {
    server: ServerConfigVariant,
    index: uint,
}

impl RoundRobin {
    pub fn new(config: ServerConfigVariant) -> RoundRobin {
        RoundRobin {
            server: config,
            index: 0u,
        }
    }
}

impl LoadBalancer for RoundRobin {
    fn pick_server<'a>(&'a mut self) -> &'a ServerConfig {
        match self.server {
            SingleServer(ref s) => s,
            MultipleServer(ref slist) => {
                let ref s = slist[self.index];
                self.index = (self.index + 1) % slist.len();
                s
            }
        }
    }
}
<|endoftext|>


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

use toml::Value;

use libimagstore::storeid::StoreId;
use libimagstore::hook::Hook;
use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor;

use vcs::git::runtime::Runtime as GRuntime;

#[derive(Debug)]
pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        self.config = Some(config.clone());
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    fn access(&self, id: &StoreId) -> HookResult<()> {
        debug!("[GIT UPDATE HOOK]: {:?}", id);
        Ok(())
    }

}

<commit_msg>impl Debug for UpdateHook<commit_after>use std::path::PathBuf;

use toml::Value;

use libimagstore::storeid::StoreId;
use libimagstore::hook::Hook;
use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor;

use vcs::git::runtime::Runtime as GRuntime;

pub struct UpdateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl UpdateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> UpdateHook {
        UpdateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for UpdateHook {
    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "UpdateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for UpdateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_update"
    }

    fn set_config(&mut self, config: &Value) {
        self.config = Some(config.clone());
    }

}

impl HookDataAccessorProvider for UpdateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for UpdateHook {

    fn access(&self, id: &StoreId) -> HookResult<()> {
        debug!("[GIT UPDATE HOOK]: {:?}", id);
        Ok(())
    }

}

<|endoftext|>


<commit_before>use core::pin::Pin;
use core::task::{Context, Poll};
use futures_core::future::Future;
use futures_core::stream::Stream;
use futures_sink::Sink;

/// Combines two different futures, streams, or sinks having the same associated types into a single
/// type.
#[derive(Debug, Clone)]
pub enum Either<A, B> {
    /// First branch of the type
    Left(A),
    /// Second branch of the type
    Right(B),
}

impl<A, B, T> Either<(T, A), (T, B)> {
    /// Factor out a homogeneous type from an either of pairs.
    ///
    /// Here, the homogeneous type is the first element of the pairs.
    pub fn factor_first(self) -> (T, Either<A, B>) {
        match self {
            Either::Left((x, a)) => (x, Either::Left(a)),
            Either::Right((x, b)) => (x, Either::Right(b)),
        }
    }
}

impl<A, B, T> Either<(A, T), (B, T)> {
    /// Factor out a homogeneous type from an either of pairs.
    ///
    /// Here, the homogeneous type is the second element of the pairs.
    pub fn factor_second(self) -> (Either<A, B>, T) {
        match self {
            Either::Left((a, x)) => (Either::Left(a), x),
            Either::Right((b, x)) => (Either::Right(b), x),
        }
    }
}

impl<T> Either<T, T> {
    /// Extract the value of an either over two equivalent types.
    pub fn into_inner(self) -> T {
        match self {
            Either::Left(x) => x,
            Either::Right(x) => x,
        }
    }
}

impl<A, B> Future for Either<A, B>
where
    A: Future,
    B: Future<Output = A::Output>,
{
    type Output = A::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<A::Output> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(a) => Pin::new_unchecked(a).poll(cx),
                Either::Right(b) => Pin::new_unchecked(b).poll(cx),
            }
        }
    }
}

impl<A, B> Stream for Either<A, B>
where
    A: Stream,
    B: Stream<Item = A::Item>,
{
    type Item = A::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<A::Item>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(a) => Pin::new_unchecked(a).poll_next(cx),
                Either::Right(b) => Pin::new_unchecked(b).poll_next(cx),
            }
        }
    }
}

impl<A, B, Item> Sink<Item> for Either<A, B>
where
    A: Sink<Item>,
    B: Sink<Item, SinkError = A::SinkError>,
{
    type SinkError = A::SinkError;

    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::SinkError>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll_ready(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll_ready(cx),
            }
        }
    }

    fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::SinkError> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).start_send(item),
                Either::Right(x) => Pin::new_unchecked(x).start_send(item),
            }
        }
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::SinkError>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx),
            }
        }
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::SinkError>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll_close(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll_close(cx),
            }
        }
    }
}
<commit_msg>impl some traits for Either<commit_after>use core::pin::Pin;
use core::task::{Context, Poll};
use futures_core::future::{FusedFuture, Future};
use futures_core::stream::{FusedStream, Stream};
use futures_sink::Sink;

/// Combines two different futures, streams, or sinks having the same associated types into a single
/// type.
#[derive(Debug, Clone)]
pub enum Either<A, B> {
    /// First branch of the type
    Left(A),
    /// Second branch of the type
    Right(B),
}

impl<A, B, T> Either<(T, A), (T, B)> {
    /// Factor out a homogeneous type from an either of pairs.
    ///
    /// Here, the homogeneous type is the first element of the pairs.
    pub fn factor_first(self) -> (T, Either<A, B>) {
        match self {
            Either::Left((x, a)) => (x, Either::Left(a)),
            Either::Right((x, b)) => (x, Either::Right(b)),
        }
    }
}

impl<A, B, T> Either<(A, T), (B, T)> {
    /// Factor out a homogeneous type from an either of pairs.
    ///
    /// Here, the homogeneous type is the second element of the pairs.
    pub fn factor_second(self) -> (Either<A, B>, T) {
        match self {
            Either::Left((a, x)) => (Either::Left(a), x),
            Either::Right((b, x)) => (Either::Right(b), x),
        }
    }
}

impl<T> Either<T, T> {
    /// Extract the value of an either over two equivalent types.
    pub fn into_inner(self) -> T {
        match self {
            Either::Left(x) => x,
            Either::Right(x) => x,
        }
    }
}

impl<A, B> Future for Either<A, B>
where
    A: Future,
    B: Future<Output = A::Output>,
{
    type Output = A::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<A::Output> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll(cx),
            }
        }
    }
}

impl<A, B> FusedFuture for Either<A, B>
where
    A: FusedFuture,
    B: FusedFuture,
{
    fn is_terminated(&self) -> bool {
        match self {
            Either::Left(x) => x.is_terminated(),
            Either::Right(x) => x.is_terminated(),
        }
    }
}

impl<A, B> Stream for Either<A, B>
where
    A: Stream,
    B: Stream<Item = A::Item>,
{
    type Item = A::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<A::Item>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll_next(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll_next(cx),
            }
        }
    }
}

impl<A, B> FusedStream for Either<A, B>
where
    A: FusedStream,
    B: FusedStream,
{
    fn is_terminated(&self) -> bool {
        match self {
            Either::Left(x) => x.is_terminated(),
            Either::Right(x) => x.is_terminated(),
        }
    }
}

impl<A, B, Item> Sink<Item> for Either<A, B>
where
    A: Sink<Item>,
    B: Sink<Item, SinkError = A::SinkError>,
{
    type SinkError = A::SinkError;

    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::SinkError>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll_ready(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll_ready(cx),
            }
        }
    }

    fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::SinkError> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).start_send(item),
                Either::Right(x) => Pin::new_unchecked(x).start_send(item),
            }
        }
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::SinkError>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx),
            }
        }
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::SinkError>> {
        unsafe {
            match self.get_unchecked_mut() {
                Either::Left(x) => Pin::new_unchecked(x).poll_close(cx),
                Either::Right(x) => Pin::new_unchecked(x).poll_close(cx),
            }
        }
    }
}

#[cfg(feature = "std")]
mod if_std {
    use super::Either;
    use core::pin::Pin;
    use core::task::{Context, Poll};
    use futures_io::{
        AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, Initializer, IoSlice, IoSliceMut, Result,
        SeekFrom,
    };

    impl<A, B> AsyncRead for Either<A, B>
    where
        A: AsyncRead,
        B: AsyncRead,
    {
        unsafe fn initializer(&self) -> Initializer {
            match self {
                Either::Left(x) => x.initializer(),
                Either::Right(x) => x.initializer(),
            }
        }

        fn poll_read(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            buf: &mut [u8],
        ) -> Poll<Result<usize>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_read(cx, buf),
                    Either::Right(x) => Pin::new_unchecked(x).poll_read(cx, buf),
                }
            }
        }

        fn poll_read_vectored(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            bufs: &mut [IoSliceMut<'_>],
        ) -> Poll<Result<usize>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs),
                    Either::Right(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs),
                }
            }
        }
    }

    impl<A, B> AsyncWrite for Either<A, B>
    where
        A: AsyncWrite,
        B: AsyncWrite,
    {
        fn poll_write(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            buf: &[u8],
        ) -> Poll<Result<usize>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_write(cx, buf),
                    Either::Right(x) => Pin::new_unchecked(x).poll_write(cx, buf),
                }
            }
        }

        fn poll_write_vectored(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            bufs: &[IoSlice<'_>],
        ) -> Poll<Result<usize>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs),
                    Either::Right(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs),
                }
            }
        }

        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx),
                    Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx),
                }
            }
        }

        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_close(cx),
                    Either::Right(x) => Pin::new_unchecked(x).poll_close(cx),
                }
            }
        }
    }

    impl<A, B> AsyncSeek for Either<A, B>
    where
        A: AsyncSeek,
        B: AsyncSeek,
    {
        fn poll_seek(
            self: Pin<&mut Self>,
            cx: &mut Context<'_>,
            pos: SeekFrom,
        ) -> Poll<Result<u64>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_seek(cx, pos),
                    Either::Right(x) => Pin::new_unchecked(x).poll_seek(cx, pos),
                }
            }
        }
    }

    impl<A, B> AsyncBufRead for Either<A, B>
    where
        A: AsyncBufRead,
        B: AsyncBufRead,
    {
        fn poll_fill_buf<'a>(
            self: Pin<&'a mut Self>,
            cx: &mut Context<'_>,
        ) -> Poll<Result<&'a [u8]>> {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).poll_fill_buf(cx),
                    Either::Right(x) => Pin::new_unchecked(x).poll_fill_buf(cx),
                }
            }
        }

        fn consume(self: Pin<&mut Self>, amt: usize) {
            unsafe {
                match self.get_unchecked_mut() {
                    Either::Left(x) => Pin::new_unchecked(x).consume(amt),
                    Either::Right(x) => Pin::new_unchecked(x).consume(amt),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::win32::menu as mh;
use crate::NwgError;
use super::{ControlBase, ControlHandle};
use std::ptr;

const NOT_BOUND: &'static str = "Menu/MenuItem is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: Menu/MenuItem handle is not HMENU!";

bitflags! {
    /**
        Menu flags to use with the `Menu::popup_with_flags` function.
        Using `PopupMenuFlags::empty` is the same as `ALIGN_LEFT|ALIGN_TOP|LEFT_BUTTON`

        Aligment flags:

        * ALIGN_LEFT:     Positions the shortcut menu so that its left side is aligned with the coordinate specified by the x parameter.
        * ALIGN_H_CENTER: Centers the shortcut menu horizontally relative to the coordinate specified by the x parameter.
        * ALIGN_RIGHT:    Positions the shortcut menu so that its right side is aligned with the coordinate specified by the x parameter.
        * ALIGN_BOTTOM:   Positions the shortcut menu so that its bottom side is aligned with the coordinate specified by the y parameter.
        * ALIGN_TOP:      Positions the shortcut menu so that its top side is aligned with the coordinate specified by the y parameter.
        * ALIGN_V_CENTER: Centers the shortcut menu vertically relative to the coordinate specified by the y parameter.

        Button flags:

        * LEFT_BUTTON:    The user can select menu items with only the left mouse button.
        * RIGHT_BUTTON:   The user can select menu items with both the left **AND** right mouse buttons.

        Animations flags:

        * ANIMATE_NONE:   Displays menu without animation.
        * ANIMATE_RIGHT_TO_LEFT:  Animates the menu from right to left.
        * ANIMATE_LEFT_TO_RIGHT:  Animates the menu from left to right.
        * ANIMATE_BOTTOM_TO_TOP:  Animates the menu from bottom to top.
        * ANIMATE_TOP_TO_BOTTOM: Animates the menu from top to bottom.
    */
    pub struct PopupMenuFlags: u32 {
        const ALIGN_LEFT = 0x0000;
        const ALIGN_H_CENTER = 0x0004;
        const ALIGN_RIGHT = 0x0008;

        const ALIGN_BOTTOM = 0x0020;
        const ALIGN_TOP = 0x0000;
        const ALIGN_V_CENTER = 0x0010;

        const LEFT_BUTTON = 0x0000;
        const RIGHT_BUTTON = 0x0002;

        const ANIMATE_NONE = 0x4000;
        const ANIMATE_RIGHT_TO_LEFT = 0x8000;
        const ANIMATE_LEFT_TO_RIGHT = 0x4000;
        const ANIMATE_BOTTOM_TO_TOP = 0x2000;
        const ANIMATE_TOP_TO_BOTTOM = 0x1000;
    }
}

/**
    A windows menu. Can represent a menu in a window menubar, a context menu, or a submenu in another menu

    Requires the `menu` feature.

    **Builder parameters:**
      - text: The text of the menu
      - disabled: If the menu can be selected by the user
      - popup: The menu is a context menu
      - parent: A top level window, a menu or None. With a top level window, the menu is added to the menu bar if popup is set to false.

    **Control events:**
      - OnMenuOpen: Sent when a drop-down menu or submenu is about to become active.
      - OnMenuHover: When the user hovers the menu
      - OnMenuEnter: When the user enters the menu. Technically, when the user enters the menu modal loop.
      - OnMenuExit: When the menu is closed. Technically, when the user exits the menu modal loop.

    **Menu Access Keys**

    Menu can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
        nwg::Menu::builder()
            .text("&Hello")
            .disabled(false)
            .parent(window)
            .build(menu)
    }
    ```
*/
#[derive(Default, PartialEq, Eq)]
pub struct Menu {
    pub handle: ControlHandle
}

impl Menu {

    pub fn builder<'a>() -> MenuBuilder<'a> {
        MenuBuilder {
            text: "Menu",
            disabled: false,
            popup: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return true; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::is_menu_enabled(parent_handle, handle) }
    }

    /// Enable or disable the control
    /// A popup menu cannot be disabled
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::enable_menu(parent_handle, handle, v); }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup_with_flags(&self, x: i32, y: i32, flags: PopupMenuFlags) {
        use winapi::um::winuser::{TrackPopupMenu, SetForegroundWindow};
        use winapi::ctypes::c_int;

        if self.handle.blank() { panic!("Menu is not bound"); }
        let (parent_handle, handle) = match self.handle.pop_hmenu() {
            Some(v) => v,
            None => { return; }
        };

        unsafe {
            SetForegroundWindow(parent_handle);
            TrackPopupMenu(
                handle,
                flags.bits(),
                x as c_int,
                y as c_int,
                0,
                parent_handle,
                ptr::null()
            );
        }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup(&self, x: i32, y: i32) {
        self.popup_with_flags(x, y, PopupMenuFlags::empty())
    }

}

impl Drop for Menu {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuBuilder<'a> {
    text: &'a str,
    disabled: bool,
    popup: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn popup(mut self, popup: bool) -> MenuBuilder<'a> {
        self.popup = popup;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, menu: &mut Menu) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        menu.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(false)
            .popup(self.popup)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            menu.set_enabled(false)
        }

        Ok(())
    }
}

/**
    A windows menu item. Can be added to a menubar or another menu.

    Requires the `menu` feature.

   **Builder parameters:**
      - text: The text of the menu item
      - disabled: If the item can be selected by the user
      - check: If the item should have a check mark next to it.
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuItemSelected: When a menu item is selected. This can be done by clicking or using the hot-key.
      - OnMenuHover: When the user hovers the menu

    **Menu Access Keys**

    Just like Menus, menu items can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu_item(item: &mut nwg::MenuItem, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuItem::builder()
            .text("&Hello")
            .disabled(true)
            .parent(menu)
            .build(item)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuItem {
    pub handle: ControlHandle
}

impl MenuItem {

    pub fn builder<'a>() -> MenuItemBuilder<'a> {
        MenuItemBuilder {
            text: "Menu Item",
            disabled: false,
            check: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::is_menuitem_enabled(parent_handle, None, Some(id)) }
    }

    /// Enable or disable the control
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::enable_menuitem(parent_handle, None, Some(id), v); }
    }

    /// Sets the check state of a menu item
    pub fn set_checked(&self, check: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::check_menu_item(parent_handle, id, check); }
    }

    /// Returns the check state of a menu item
    pub fn checked(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::menu_item_checked(parent_handle, id) }
    }

}

impl Drop for MenuItem {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuItemBuilder<'a> {
    text: &'a str,
    disabled: bool,
    check: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuItemBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuItemBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuItemBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn check(mut self, check: bool) -> MenuItemBuilder<'a> {
        self.check = check;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuItemBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, item: &mut MenuItem) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        item.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(true)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            item.set_enabled(false);
        }

        if self.check {
            item.set_checked(true);
        }

        Ok(())
    }
}

/**
    A menu separator. Can be added between two menu item to separte them. Cannot be added to a menubar.

    Requires the `menu` feature.

    **Builder parameters:**
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuHover: When the user hovers the menu

    ```rust
    use native_windows_gui as nwg;

    fn separator(sep: &mut nwg::MenuSeparator, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuSeparator::builder()
            .parent(menu)
            .build(sep)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuSeparator {
    pub handle: ControlHandle
}

impl MenuSeparator {

    pub fn builder() -> MenuSeparatorBuilder {
        MenuSeparatorBuilder {
            parent: None
        }
    }

}

pub struct MenuSeparatorBuilder {
    parent: Option<ControlHandle>
}

impl MenuSeparatorBuilder {

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuSeparatorBuilder {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, sep: &mut MenuSeparator) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        sep.handle = ControlBase::build_hmenu()
            .separator(true)
            .parent(self.parent.unwrap())
            .build()?;

        Ok(())
    }
}

impl Drop for MenuSeparator {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}
<commit_msg>Add documentation for shortcut labels on MenuItem<commit_after>use crate::win32::menu as mh;
use crate::NwgError;
use super::{ControlBase, ControlHandle};
use std::ptr;

const NOT_BOUND: &'static str = "Menu/MenuItem is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: Menu/MenuItem handle is not HMENU!";

bitflags! {
    /**
        Menu flags to use with the `Menu::popup_with_flags` function.
        Using `PopupMenuFlags::empty` is the same as `ALIGN_LEFT|ALIGN_TOP|LEFT_BUTTON`

        Aligment flags:

        * ALIGN_LEFT:     Positions the shortcut menu so that its left side is aligned with the coordinate specified by the x parameter.
        * ALIGN_H_CENTER: Centers the shortcut menu horizontally relative to the coordinate specified by the x parameter.
        * ALIGN_RIGHT:    Positions the shortcut menu so that its right side is aligned with the coordinate specified by the x parameter.
        * ALIGN_BOTTOM:   Positions the shortcut menu so that its bottom side is aligned with the coordinate specified by the y parameter.
        * ALIGN_TOP:      Positions the shortcut menu so that its top side is aligned with the coordinate specified by the y parameter.
        * ALIGN_V_CENTER: Centers the shortcut menu vertically relative to the coordinate specified by the y parameter.

        Button flags:

        * LEFT_BUTTON:    The user can select menu items with only the left mouse button.
        * RIGHT_BUTTON:   The user can select menu items with both the left **AND** right mouse buttons.

        Animations flags:

        * ANIMATE_NONE:   Displays menu without animation.
        * ANIMATE_RIGHT_TO_LEFT:  Animates the menu from right to left.
        * ANIMATE_LEFT_TO_RIGHT:  Animates the menu from left to right.
        * ANIMATE_BOTTOM_TO_TOP:  Animates the menu from bottom to top.
        * ANIMATE_TOP_TO_BOTTOM: Animates the menu from top to bottom.
    */
    pub struct PopupMenuFlags: u32 {
        const ALIGN_LEFT = 0x0000;
        const ALIGN_H_CENTER = 0x0004;
        const ALIGN_RIGHT = 0x0008;

        const ALIGN_BOTTOM = 0x0020;
        const ALIGN_TOP = 0x0000;
        const ALIGN_V_CENTER = 0x0010;

        const LEFT_BUTTON = 0x0000;
        const RIGHT_BUTTON = 0x0002;

        const ANIMATE_NONE = 0x4000;
        const ANIMATE_RIGHT_TO_LEFT = 0x8000;
        const ANIMATE_LEFT_TO_RIGHT = 0x4000;
        const ANIMATE_BOTTOM_TO_TOP = 0x2000;
        const ANIMATE_TOP_TO_BOTTOM = 0x1000;
    }
}

/**
    A windows menu. Can represent a menu in a window menubar, a context menu, or a submenu in another menu

    Requires the `menu` feature.

    **Builder parameters:**
      - text: The text of the menu, including access key and shortcut label
      - disabled: If the menu can be selected by the user
      - popup: The menu is a context menu
      - parent: A top level window, a menu or None. With a top level window, the menu is added to the menu bar if popup is set to false.

    **Control events:**
      - OnMenuOpen: Sent when a drop-down menu or submenu is about to become active.
      - OnMenuHover: When the user hovers the menu
      - OnMenuEnter: When the user enters the menu. Technically, when the user enters the menu modal loop.
      - OnMenuExit: When the menu is closed. Technically, when the user exits the menu modal loop.

    **Menu Access Keys**

    Menu can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
        nwg::Menu::builder()
            .text("&Hello")
            .disabled(false)
            .parent(window)
            .build(menu)
    }
    ```

    **Shortcut Label**

    A shortcut label like "Ctrl+O" can be added with the `text` field. By prefixing the shortcut with a tab character `\t` and
    adding it as a suffix to the text label, it will be rendered right-aligned in the menu item.

    For example, an "Exit" menu item could have both an access key "E" and a shortcut label "Alt+F4" with `text: "&Exit\tAlt+F4"`.

    **note:** This will only add a text label to the menu item, the keyboard handling must be done through other means.

    ```rust
    use native_windows_gui as nwg;

    fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
        nwg::Menu::builder()
            .text("&Exit\tAlt+F4")
            .disabled(false)
            .parent(window)
            .build(menu)
    }
    ```
*/
#[derive(Default, PartialEq, Eq)]
pub struct Menu {
    pub handle: ControlHandle
}

impl Menu {

    pub fn builder<'a>() -> MenuBuilder<'a> {
        MenuBuilder {
            text: "Menu",
            disabled: false,
            popup: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return true; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::is_menu_enabled(parent_handle, handle) }
    }

    /// Enable or disable the control
    /// A popup menu cannot be disabled
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::enable_menu(parent_handle, handle, v); }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup_with_flags(&self, x: i32, y: i32, flags: PopupMenuFlags) {
        use winapi::um::winuser::{TrackPopupMenu, SetForegroundWindow};
        use winapi::ctypes::c_int;

        if self.handle.blank() { panic!("Menu is not bound"); }
        let (parent_handle, handle) = match self.handle.pop_hmenu() {
            Some(v) => v,
            None => { return; }
        };

        unsafe {
            SetForegroundWindow(parent_handle);
            TrackPopupMenu(
                handle,
                flags.bits(),
                x as c_int,
                y as c_int,
                0,
                parent_handle,
                ptr::null()
            );
        }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup(&self, x: i32, y: i32) {
        self.popup_with_flags(x, y, PopupMenuFlags::empty())
    }

}

impl Drop for Menu {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuBuilder<'a> {
    text: &'a str,
    disabled: bool,
    popup: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn popup(mut self, popup: bool) -> MenuBuilder<'a> {
        self.popup = popup;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, menu: &mut Menu) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        menu.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(false)
            .popup(self.popup)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            menu.set_enabled(false)
        }

        Ok(())
    }
}

/**
    A windows menu item. Can be added to a menubar or another menu.

    Requires the `menu` feature.

   **Builder parameters:**
      - text: The text of the menu item
      - disabled: If the item can be selected by the user
      - check: If the item should have a check mark next to it.
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuItemSelected: When a menu item is selected. This can be done by clicking or using the hot-key.
      - OnMenuHover: When the user hovers the menu

    **Menu Access Keys**

    Just like Menus, menu items can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu_item(item: &mut nwg::MenuItem, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuItem::builder()
            .text("&Hello")
            .disabled(true)
            .parent(menu)
            .build(item)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuItem {
    pub handle: ControlHandle
}

impl MenuItem {

    pub fn builder<'a>() -> MenuItemBuilder<'a> {
        MenuItemBuilder {
            text: "Menu Item",
            disabled: false,
            check: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::is_menuitem_enabled(parent_handle, None, Some(id)) }
    }

    /// Enable or disable the control
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::enable_menuitem(parent_handle, None, Some(id), v); }
    }

    /// Sets the check state of a menu item
    pub fn set_checked(&self, check: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::check_menu_item(parent_handle, id, check); }
    }

    /// Returns the check state of a menu item
    pub fn checked(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::menu_item_checked(parent_handle, id) }
    }

}

impl Drop for MenuItem {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuItemBuilder<'a> {
    text: &'a str,
    disabled: bool,
    check: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuItemBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuItemBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuItemBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn check(mut self, check: bool) -> MenuItemBuilder<'a> {
        self.check = check;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuItemBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, item: &mut MenuItem) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        item.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(true)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            item.set_enabled(false);
        }

        if self.check {
            item.set_checked(true);
        }

        Ok(())
    }
}

/**
    A menu separator. Can be added between two menu item to separte them. Cannot be added to a menubar.

    Requires the `menu` feature.

    **Builder parameters:**
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuHover: When the user hovers the menu

    ```rust
    use native_windows_gui as nwg;

    fn separator(sep: &mut nwg::MenuSeparator, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuSeparator::builder()
            .parent(menu)
            .build(sep)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuSeparator {
    pub handle: ControlHandle
}

impl MenuSeparator {

    pub fn builder() -> MenuSeparatorBuilder {
        MenuSeparatorBuilder {
            parent: None
        }
    }

}

pub struct MenuSeparatorBuilder {
    parent: Option<ControlHandle>
}

impl MenuSeparatorBuilder {

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuSeparatorBuilder {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, sep: &mut MenuSeparator) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        sep.handle = ControlBase::build_hmenu()
            .separator(true)
            .parent(self.parent.unwrap())
            .build()?;

        Ok(())
    }
}

impl Drop for MenuSeparator {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}
<|endoftext|>