<commit_before>use crate::schema::*;
use diesel::result::DatabaseErrorKind::{ForeignKeyViolation, UniqueViolation};
use diesel::result::Error::DatabaseError;
use diesel::*;

#[test]
fn unique_constraints_are_detected() {
    let connection = connection();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(&connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(1, "Jim"))
        .execute(&connection);
    assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn unique_constraints_report_correct_constraint_name() {
    let connection = connection();
    connection
        .execute("CREATE UNIQUE INDEX users_name ON users (name)")
        .unwrap();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(&connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(2, "Sean"))
        .execute(&connection);
    match failure {
        Err(DatabaseError(UniqueViolation, e)) => {
            assert_eq!(Some("users"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("users_name"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(UniqueViolation, e))",
            failure
        ),
    };
}

macro_rules! try_no_coerce {
    ($e:expr) => {{
        match $e {
            Ok(e) => e,
            Err(e) => return Err(e),
        }
    }};
}

#[test]
fn cached_prepared_statements_can_be_reused_after_error() {
    let connection = connection_without_transaction();
    let user = User::new(1, "Sean");
    let query = insert_into(users::table).values(&user);

    connection.test_transaction(|| {
        try_no_coerce!(query.execute(&connection));

        let failure = query.execute(&connection);
        assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
        Ok(())
    });

    connection.test_transaction(|| query.execute(&connection));
}

#[test]
fn foreign_key_violation_detected() {
    let connection = connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(&connection);
    assert_matches!(failure, Err(DatabaseError(ForeignKeyViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn foreign_key_violation_correct_constraint_name() {
    let connection = connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(&connection);
    match failure {
        Err(DatabaseError(ForeignKeyViolation, e)) => {
            assert_eq!(Some("fk_tests"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("fk_tests_fk_id_fkey"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(ForeignKeyViolation, e))",
            failure
        ),
    }
}

#[test]
#[cfg(feature = "postgres")]
fn isolation_errors_are_detected() {
    use diesel::result::DatabaseErrorKind::SerializationFailure;
    use diesel::result::Error::DatabaseError;
    use std::sync::{Arc, Barrier};
    use std::thread;

    table! {
        #[sql_name = "isolation_errors_are_detected"]
        isolation_example {
            id -> Serial,
            class -> Integer,
        }
    }

    let conn = connection_without_transaction();

    sql_query("DROP TABLE IF EXISTS isolation_errors_are_detected;")
        .execute(&conn)
        .unwrap();
    sql_query(
        r#"
        CREATE TABLE isolation_errors_are_detected (
            id SERIAL PRIMARY KEY,
            class INTEGER NOT NULL
        )
    "#,
    )
    .execute(&conn)
    .unwrap();

    insert_into(isolation_example::table)
        .values(&vec![
            isolation_example::class.eq(1),
            isolation_example::class.eq(2),
        ])
        .execute(&conn)
        .unwrap();

    let barrier = Arc::new(Barrier::new(2));
    let threads = (1..3)
        .map(|i| {
            let barrier = barrier.clone();
            thread::spawn(move || {
                let conn = connection_without_transaction();

                conn.build_transaction().serializable().run(|| {
                    let _ = isolation_example::table
                        .filter(isolation_example::class.eq(i))
                        .count()
                        .execute(&conn)?;

                    barrier.wait();

                    let other_i = if i == 1 { 2 } else { 1 };
                    insert_into(isolation_example::table)
                        .values(isolation_example::class.eq(other_i))
                        .execute(&conn)
                })
            })
        })
        .collect::<Vec<_>>();

    let mut results = threads
        .into_iter()
        .map(|t| t.join().unwrap())
        .collect::<Vec<_>>();

    results.sort_by_key(|r| r.is_err());

    assert_matches!(results[0], Ok(_));
    assert_matches!(results[1], Err(DatabaseError(SerializationFailure, _)));
}

#[test]
#[cfg(not(feature = "sqlite"))]
fn read_only_errors_are_detected() {
    use diesel::result::DatabaseErrorKind::ReadOnlyTransaction;

    let conn = connection_without_transaction();
    conn.execute("START TRANSACTION READ ONLY").unwrap();

    let result = users::table.for_update().load::<User>(&conn);

    assert_matches!(result, Err(DatabaseError(ReadOnlyTransaction, _)));
}
<commit_msg>Introduce tests for 'CheckViolation' and 'NotNullViolation'<commit_after>use crate::schema::*;
use diesel::result::DatabaseErrorKind::{
    CheckViolation, ForeignKeyViolation, NotNullViolation, UniqueViolation,
};
use diesel::result::Error::DatabaseError;
use diesel::*;

#[test]
fn unique_constraints_are_detected() {
    let connection = connection();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(&connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(1, "Jim"))
        .execute(&connection);
    assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn unique_constraints_report_correct_constraint_name() {
    let connection = connection();
    connection
        .execute("CREATE UNIQUE INDEX users_name ON users (name)")
        .unwrap();
    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(&connection)
        .unwrap();

    let failure = insert_into(users::table)
        .values(&User::new(2, "Sean"))
        .execute(&connection);
    match failure {
        Err(DatabaseError(UniqueViolation, e)) => {
            assert_eq!(Some("users"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("users_name"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(UniqueViolation, e))",
            failure
        ),
    };
}

macro_rules! try_no_coerce {
    ($e:expr) => {{
        match $e {
            Ok(e) => e,
            Err(e) => return Err(e),
        }
    }};
}

#[test]
fn cached_prepared_statements_can_be_reused_after_error() {
    let connection = connection_without_transaction();
    let user = User::new(1, "Sean");
    let query = insert_into(users::table).values(&user);

    connection.test_transaction(|| {
        try_no_coerce!(query.execute(&connection));

        let failure = query.execute(&connection);
        assert_matches!(failure, Err(DatabaseError(UniqueViolation, _)));
        Ok(())
    });

    connection.test_transaction(|| query.execute(&connection));
}

#[test]
fn foreign_key_violation_detected() {
    let connection = connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(&connection);
    assert_matches!(failure, Err(DatabaseError(ForeignKeyViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn foreign_key_violation_correct_constraint_name() {
    let connection = connection();

    let failure = insert_into(fk_tests::table)
        .values(&FkTest::new(1, 100))
        .execute(&connection);
    match failure {
        Err(DatabaseError(ForeignKeyViolation, e)) => {
            assert_eq!(Some("fk_tests"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("fk_tests_fk_id_fkey"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(ForeignKeyViolation, e))",
            failure
        ),
    }
}

#[test]
#[cfg(feature = "postgres")]
fn isolation_errors_are_detected() {
    use diesel::result::DatabaseErrorKind::SerializationFailure;
    use diesel::result::Error::DatabaseError;
    use std::sync::{Arc, Barrier};
    use std::thread;

    table! {
        #[sql_name = "isolation_errors_are_detected"]
        isolation_example {
            id -> Serial,
            class -> Integer,
        }
    }

    let conn = connection_without_transaction();

    sql_query("DROP TABLE IF EXISTS isolation_errors_are_detected;")
        .execute(&conn)
        .unwrap();
    sql_query(
        r#"
        CREATE TABLE isolation_errors_are_detected (
            id SERIAL PRIMARY KEY,
            class INTEGER NOT NULL
        )
    "#,
    )
    .execute(&conn)
    .unwrap();

    insert_into(isolation_example::table)
        .values(&vec![
            isolation_example::class.eq(1),
            isolation_example::class.eq(2),
        ])
        .execute(&conn)
        .unwrap();

    let barrier = Arc::new(Barrier::new(2));
    let threads = (1..3)
        .map(|i| {
            let barrier = barrier.clone();
            thread::spawn(move || {
                let conn = connection_without_transaction();

                conn.build_transaction().serializable().run(|| {
                    let _ = isolation_example::table
                        .filter(isolation_example::class.eq(i))
                        .count()
                        .execute(&conn)?;

                    barrier.wait();

                    let other_i = if i == 1 { 2 } else { 1 };
                    insert_into(isolation_example::table)
                        .values(isolation_example::class.eq(other_i))
                        .execute(&conn)
                })
            })
        })
        .collect::<Vec<_>>();

    let mut results = threads
        .into_iter()
        .map(|t| t.join().unwrap())
        .collect::<Vec<_>>();

    results.sort_by_key(|r| r.is_err());

    assert_matches!(results[0], Ok(_));
    assert_matches!(results[1], Err(DatabaseError(SerializationFailure, _)));
}

#[test]
#[cfg(not(feature = "sqlite"))]
fn read_only_errors_are_detected() {
    use diesel::result::DatabaseErrorKind::ReadOnlyTransaction;

    let conn = connection_without_transaction();
    conn.execute("START TRANSACTION READ ONLY").unwrap();

    let result = users::table.for_update().load::<User>(&conn);

    assert_matches!(result, Err(DatabaseError(ReadOnlyTransaction, _)));
}

#[test]
fn not_null_constraints_are_detected() {
    let connection = connection();

    let failure = insert_into(users::table)
        .values(users::columns::hair_color.eq("black"))
        .execute(&connection);

    assert_matches!(failure, Err(DatabaseError(NotNullViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn not_null_constraints_correct_column_name() {
    let connection = connection();

    let failure = insert_into(users::table)
        .values(users::columns::hair_color.eq("black"))
        .execute(&connection);

    match failure {
        Err(DatabaseError(NotNullViolation, e)) => {
            assert_eq!(Some("users"), e.table_name());
            assert_eq!(Some("name"), e.column_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(NotNullViolation, e))",
            failure
        ),
    };
}

#[test]
fn check_constraints_constraints_are_detected() {
    let connection = connection();

    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(&connection)
        .unwrap();

    let failure = insert_into(pokes::table)
        .values((
            pokes::columns::user_id.eq(1),
            pokes::columns::poke_count.eq(-1),
        ))
        .execute(&connection);

    assert_matches!(failure, Err(DatabaseError(CheckViolation, _)));
}

#[test]
#[cfg(feature = "postgres")]
fn check_constraints_correct_constraint_name() {
    let connection = connection();

    insert_into(users::table)
        .values(&User::new(1, "Sean"))
        .execute(&connection)
        .unwrap();

    let failure = insert_into(pokes::table)
        .values((
            pokes::columns::user_id.eq(1),
            pokes::columns::poke_count.eq(-1),
        ))
        .execute(&connection);

    match failure {
        Err(DatabaseError(CheckViolation, e)) => {
            assert_eq!(Some("pokes"), e.table_name());
            assert_eq!(None, e.column_name());
            assert_eq!(Some("pokes_poke_count_check"), e.constraint_name());
        }
        _ => panic!(
            "{:?} did not match Err(DatabaseError(CheckViolation, e))",
            failure
        ),
    };
}
<|endoftext|>


<commit_before>use schema::*;
use diesel::*;

#[test]
fn filter_by_int_equality() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();
    let sean_id = find_user_by_name("Sean", &connection).id;
    let tess_id = find_user_by_name("Tess", &connection).id;
    let unused_id = sean_id + tess_id;

    let sean = User::new(sean_id, "Sean");
    let tess = User::new(tess_id, "Tess");
    assert_eq!(Ok(sean), users.filter(id.eq(sean_id)).first(&connection));
    assert_eq!(Ok(tess), users.filter(id.eq(tess_id)).first(&connection));
    assert_eq!(Err(NotFound), users.filter(id.eq(unused_id)).first::<User>(&connection));
}

#[test]
fn filter_by_string_equality() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();

    let sean = User::new(1, "Sean");
    let tess = User::new(2, "Tess");
    assert_eq!(Ok(sean), users.filter(name.eq("Sean")).first(&connection));
    assert_eq!(Ok(tess), users.filter(name.eq("Tess")).first(&connection));
    assert_eq!(Err(NotFound), users.filter(name.eq("Jim")).first::<User>(&connection));
}

#[test]
fn filter_by_equality_on_nullable_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![
        NewUser::new("Sean", Some("black")),
        NewUser::new("Tess", Some("brown")),
        NewUser::new("Jim", Some("black")),
    ];
    insert(&data).into(users).execute(&connection).unwrap();

    let data = users.load::<User>(&connection).unwrap();
    let sean = data[0].clone();
    let tess = data[1].clone();
    let jim = data[2].clone();

    let source = users.filter(hair_color.eq("black"));
    assert_eq!(vec![sean, jim], source.load(&connection).unwrap());
    let source = users.filter(hair_color.eq("brown"));
    assert_eq!(vec![tess], source.load(&connection).unwrap());
}

#[test]
fn filter_by_is_not_null_on_nullable_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![
        NewUser::new("Derek", Some("red")),
        NewUser::new("Gordon", None),
    ];
    insert(&data).into(users).execute(&connection).unwrap();
    let data = users.load::<User>(&connection).unwrap();
    let derek = data[0].clone();

    let source = users.filter(hair_color.is_not_null());
    assert_eq!(vec![derek], source.load(&connection).unwrap());
}

#[test]
fn filter_by_is_null_on_nullable_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![
        NewUser::new("Derek", Some("red")),
        NewUser::new("Gordon", None),
    ];
    insert(&data).into(users).execute(&connection).unwrap();
    let data = users.load::<User>(&connection).unwrap();
    let gordon = data[1].clone();

    let source = users.filter(hair_color.is_null());
    assert_eq!(vec![gordon], source.load(&connection).unwrap());
}

#[test]
fn filter_after_joining() {
    use schema::users::name;

    let connection = connection_with_sean_and_tess_in_users_table();
    connection.execute("INSERT INTO posts (id, title, user_id) VALUES
                       (1, 'Hello', 1), (2, 'World', 2)")
        .unwrap();

    let sean = User::new(1, "Sean");
    let tess = User::new(2, "Tess");
    let seans_post = Post::new(1, 1, "Hello", None);
    let tess_post = Post::new(2, 2, "World", None);
    let source = users::table.inner_join(posts::table);
    assert_eq!(Ok((sean, seans_post)),
        source.filter(name.eq("Sean")).first(&connection));
    assert_eq!(Ok((tess, tess_post)),
        source.filter(name.eq("Tess")).first(&connection));
    assert_eq!(Err(NotFound),
        source.filter(name.eq("Jim")).first::<(User, Post)>(&connection));
}

#[test]
fn select_then_filter() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();

    let source = users.select(name);
    assert_eq!(Ok("Sean".to_string()),
        source.filter(name.eq("Sean")).first(&connection));
    assert_eq!(Ok("Tess".to_string()),
        source.filter(name.eq("Tess")).first(&connection));
    assert_eq!(Err(NotFound), source.filter(name.eq("Jim")).first::<String>(&connection));
}

#[test]
fn filter_then_select() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![NewUser::new("Sean", None), NewUser::new("Tess", None)];
    insert(&data).into(users).execute(&connection).unwrap();

    assert_eq!(Ok("Sean".to_string()),
        users.filter(name.eq("Sean")).select(name).first(&connection));
    assert_eq!(Ok("Tess".to_string()),
        users.filter(name.eq("Tess")).select(name).first(&connection));
    assert_eq!(Err(NotFound), users.filter(name.eq("Jim")).select(name)
                                   .first::<String>(&connection));
}

#[test]
fn filter_on_multiple_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data: &[_] = &[
        NewUser::new("Sean", Some("black")),
        NewUser::new("Sean", Some("brown")),
        NewUser::new("Sean", None),
        NewUser::new("Tess", Some("black")),
        NewUser::new("Tess", Some("brown")),
    ];
    insert(data).into(users).execute(&connection).unwrap();
    let data = users.load::<User>(&connection).unwrap();
    let black_haired_sean = data[0].clone();
    let brown_haired_sean = data[1].clone();
    let black_haired_tess = data[3].clone();
    let brown_haired_tess = data[4].clone();

    let source = users.filter(name.eq("Sean").and(hair_color.eq("black")));
    assert_eq!(vec![black_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Sean").and(hair_color.eq("brown")));
    assert_eq!(vec![brown_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess").and(hair_color.eq("black")));
    assert_eq!(vec![black_haired_tess], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess").and(hair_color.eq("brown")));
    assert_eq!(vec![brown_haired_tess], source.load(&connection).unwrap());
}

#[test]
fn filter_called_twice_means_same_thing_as_and() {
    use schema::users::dsl::*;

    let connection = connection();
    let data: &[_] = &[
        NewUser::new("Sean", Some("black")),
        NewUser::new("Sean", Some("brown")),
        NewUser::new("Sean", None),
        NewUser::new("Tess", Some("black")),
        NewUser::new("Tess", Some("brown")),
    ];
    insert(data).into(users).execute(&connection).unwrap();
    let data = users.load::<User>(&connection).unwrap();
    let black_haired_sean = data[0].clone();
    let brown_haired_sean = data[1].clone();
    let black_haired_tess = data[3].clone();
    let brown_haired_tess = data[4].clone();

    let source = users.filter(name.eq("Sean")).filter(hair_color.eq("black"));
    assert_eq!(vec![black_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Sean")).filter(hair_color.eq("brown"));
    assert_eq!(vec![brown_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess")).filter(hair_color.eq("black"));
    assert_eq!(vec![black_haired_tess], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess")).filter(hair_color.eq("brown"));
    assert_eq!(vec![brown_haired_tess], source.load(&connection).unwrap());
}

table! {
    points (x) {
        x -> Integer,
        y -> Integer,
    }
}

#[test]
fn filter_on_column_equality() {
    use self::points::dsl::*;

    let connection = connection();
    connection.execute("INSERT INTO points (x, y) VALUES (1, 1), (1, 2), (2, 2)").unwrap();

    let expected_data = vec![(1, 1), (2, 2)];
    let query = points.filter(x.eq(y));
    let data: Vec<_> = query.load(&connection).unwrap();
    assert_eq!(expected_data, data);
}

#[test]
fn filter_with_or() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();
    insert(&NewUser::new("Jim", None)).into(users).execute(&connection).unwrap();

    let expected_users = vec![User::new(1, "Sean"), User::new(2, "Tess")];
    let data: Vec<_> = users.filter(name.eq("Sean").or(name.eq("Tess")))
        .load(&connection).unwrap();

    assert_eq!(expected_users, data);
}

#[test]
fn or_doesnt_mess_with_precidence_of_previous_statements() {
    use schema::users::dsl::*;
    use diesel::expression::AsExpression;

    let connection = connection_with_sean_and_tess_in_users_table();
    let f = AsExpression::<types::Bool>::as_expression(false);
    let count = users.filter(f).filter(f.or(true))
        .count().first(&connection);

    assert_eq!(Ok(0), count);

    let count = users.filter(f.or(f).and(f.or(true)))
        .count().first(&connection);

    assert_eq!(Ok(0), count);
}

use diesel::types::VarChar;
sql_function!(lower, lower_t, (x: VarChar) -> VarChar);

#[test]
fn filter_by_boxed_predicate() {
    fn by_name(name: &str) -> Box<BoxableExpression<users::table, TestBackend, SqlType=types::Bool, SqlTypeForSelect=types::Bool>> {
        Box::new(lower(users::name).eq(name.to_string()))
    }

    let connection = connection_with_sean_and_tess_in_users_table();
    let sean = User::new(1, "Sean");
    let tess = User::new(2, "Tess");
    let queried_sean = users::table.filter(by_name("sean")).first(&connection);
    let queried_tess = users::table.filter(by_name("tess")).first(&connection);

    assert_eq!(Ok(sean), queried_sean);
    assert_eq!(Ok(tess), queried_tess);
}
<commit_msg>Order by id in filter tests<commit_after>use schema::*;
use diesel::*;

#[test]
fn filter_by_int_equality() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();
    let sean_id = find_user_by_name("Sean", &connection).id;
    let tess_id = find_user_by_name("Tess", &connection).id;
    let unused_id = sean_id + tess_id;

    let sean = User::new(sean_id, "Sean");
    let tess = User::new(tess_id, "Tess");
    assert_eq!(Ok(sean), users.filter(id.eq(sean_id)).first(&connection));
    assert_eq!(Ok(tess), users.filter(id.eq(tess_id)).first(&connection));
    assert_eq!(Err(NotFound), users.filter(id.eq(unused_id)).first::<User>(&connection));
}

#[test]
fn filter_by_string_equality() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();

    let sean = User::new(1, "Sean");
    let tess = User::new(2, "Tess");
    assert_eq!(Ok(sean), users.filter(name.eq("Sean")).first(&connection));
    assert_eq!(Ok(tess), users.filter(name.eq("Tess")).first(&connection));
    assert_eq!(Err(NotFound), users.filter(name.eq("Jim")).first::<User>(&connection));
}

#[test]
fn filter_by_equality_on_nullable_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![
        NewUser::new("Sean", Some("black")),
        NewUser::new("Tess", Some("brown")),
        NewUser::new("Jim", Some("black")),
    ];
    insert(&data).into(users).execute(&connection).unwrap();

    let data = users.order(id).load::<User>(&connection).unwrap();
    let sean = data[0].clone();
    let tess = data[1].clone();
    let jim = data[2].clone();

    let source = users.filter(hair_color.eq("black"));
    assert_eq!(vec![sean, jim], source.order(id).load(&connection).unwrap());
    let source = users.filter(hair_color.eq("brown"));
    assert_eq!(vec![tess], source.load(&connection).unwrap());
}

#[test]
fn filter_by_is_not_null_on_nullable_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![
        NewUser::new("Derek", Some("red")),
        NewUser::new("Gordon", None),
    ];
    insert(&data).into(users).execute(&connection).unwrap();
    let data = users.order(id).load::<User>(&connection).unwrap();
    let derek = data[0].clone();

    let source = users.filter(hair_color.is_not_null());
    assert_eq!(vec![derek], source.load(&connection).unwrap());
}

#[test]
fn filter_by_is_null_on_nullable_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![
        NewUser::new("Derek", Some("red")),
        NewUser::new("Gordon", None),
    ];
    insert(&data).into(users).execute(&connection).unwrap();
    let data = users.order(id).load::<User>(&connection).unwrap();
    let gordon = data[1].clone();

    let source = users.filter(hair_color.is_null());
    assert_eq!(vec![gordon], source.load(&connection).unwrap());
}

#[test]
fn filter_after_joining() {
    use schema::users::name;

    let connection = connection_with_sean_and_tess_in_users_table();
    connection.execute("INSERT INTO posts (id, title, user_id) VALUES
                       (1, 'Hello', 1), (2, 'World', 2)")
        .unwrap();

    let sean = User::new(1, "Sean");
    let tess = User::new(2, "Tess");
    let seans_post = Post::new(1, 1, "Hello", None);
    let tess_post = Post::new(2, 2, "World", None);
    let source = users::table.inner_join(posts::table);
    assert_eq!(Ok((sean, seans_post)),
        source.filter(name.eq("Sean")).first(&connection));
    assert_eq!(Ok((tess, tess_post)),
        source.filter(name.eq("Tess")).first(&connection));
    assert_eq!(Err(NotFound),
        source.filter(name.eq("Jim")).first::<(User, Post)>(&connection));
}

#[test]
fn select_then_filter() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();

    let source = users.select(name);
    assert_eq!(Ok("Sean".to_string()),
        source.filter(name.eq("Sean")).first(&connection));
    assert_eq!(Ok("Tess".to_string()),
        source.filter(name.eq("Tess")).first(&connection));
    assert_eq!(Err(NotFound), source.filter(name.eq("Jim")).first::<String>(&connection));
}

#[test]
fn filter_then_select() {
    use schema::users::dsl::*;

    let connection = connection();
    let data = vec![NewUser::new("Sean", None), NewUser::new("Tess", None)];
    insert(&data).into(users).execute(&connection).unwrap();

    assert_eq!(Ok("Sean".to_string()),
        users.filter(name.eq("Sean")).select(name).first(&connection));
    assert_eq!(Ok("Tess".to_string()),
        users.filter(name.eq("Tess")).select(name).first(&connection));
    assert_eq!(Err(NotFound), users.filter(name.eq("Jim")).select(name)
                                   .first::<String>(&connection));
}

#[test]
fn filter_on_multiple_columns() {
    use schema::users::dsl::*;

    let connection = connection();
    let data: &[_] = &[
        NewUser::new("Sean", Some("black")),
        NewUser::new("Sean", Some("brown")),
        NewUser::new("Sean", None),
        NewUser::new("Tess", Some("black")),
        NewUser::new("Tess", Some("brown")),
    ];
    insert(data).into(users).execute(&connection).unwrap();
    let data = users.order(id).load::<User>(&connection).unwrap();
    let black_haired_sean = data[0].clone();
    let brown_haired_sean = data[1].clone();
    let black_haired_tess = data[3].clone();
    let brown_haired_tess = data[4].clone();

    let source = users.filter(name.eq("Sean").and(hair_color.eq("black")));
    assert_eq!(vec![black_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Sean").and(hair_color.eq("brown")));
    assert_eq!(vec![brown_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess").and(hair_color.eq("black")));
    assert_eq!(vec![black_haired_tess], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess").and(hair_color.eq("brown")));
    assert_eq!(vec![brown_haired_tess], source.load(&connection).unwrap());
}

#[test]
fn filter_called_twice_means_same_thing_as_and() {
    use schema::users::dsl::*;

    let connection = connection();
    let data: &[_] = &[
        NewUser::new("Sean", Some("black")),
        NewUser::new("Sean", Some("brown")),
        NewUser::new("Sean", None),
        NewUser::new("Tess", Some("black")),
        NewUser::new("Tess", Some("brown")),
    ];
    insert(data).into(users).execute(&connection).unwrap();
    let data = users.order(id).load::<User>(&connection).unwrap();
    let black_haired_sean = data[0].clone();
    let brown_haired_sean = data[1].clone();
    let black_haired_tess = data[3].clone();
    let brown_haired_tess = data[4].clone();

    let source = users.filter(name.eq("Sean")).filter(hair_color.eq("black"));
    assert_eq!(vec![black_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Sean")).filter(hair_color.eq("brown"));
    assert_eq!(vec![brown_haired_sean], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess")).filter(hair_color.eq("black"));
    assert_eq!(vec![black_haired_tess], source.load(&connection).unwrap());

    let source = users.filter(name.eq("Tess")).filter(hair_color.eq("brown"));
    assert_eq!(vec![brown_haired_tess], source.load(&connection).unwrap());
}

table! {
    points (x) {
        x -> Integer,
        y -> Integer,
    }
}

#[test]
fn filter_on_column_equality() {
    use self::points::dsl::*;

    let connection = connection();
    connection.execute("INSERT INTO points (x, y) VALUES (1, 1), (1, 2), (2, 2)").unwrap();

    let expected_data = vec![(1, 1), (2, 2)];
    let query = points.order(x).filter(x.eq(y));
    let data: Vec<_> = query.load(&connection).unwrap();
    assert_eq!(expected_data, data);
}

#[test]
fn filter_with_or() {
    use schema::users::dsl::*;

    let connection = connection_with_sean_and_tess_in_users_table();
    insert(&NewUser::new("Jim", None)).into(users).execute(&connection).unwrap();

    let expected_users = vec![User::new(1, "Sean"), User::new(2, "Tess")];
    let data: Vec<_> = users.order(id)
        .filter(name.eq("Sean").or(name.eq("Tess")))
        .load(&connection).unwrap();

    assert_eq!(expected_users, data);
}

#[test]
fn or_doesnt_mess_with_precidence_of_previous_statements() {
    use schema::users::dsl::*;
    use diesel::expression::AsExpression;

    let connection = connection_with_sean_and_tess_in_users_table();
    let f = AsExpression::<types::Bool>::as_expression(false);
    let count = users.filter(f).filter(f.or(true))
        .count().first(&connection);

    assert_eq!(Ok(0), count);

    let count = users.filter(f.or(f).and(f.or(true)))
        .count().first(&connection);

    assert_eq!(Ok(0), count);
}

use diesel::types::VarChar;
sql_function!(lower, lower_t, (x: VarChar) -> VarChar);

#[test]
fn filter_by_boxed_predicate() {
    fn by_name(name: &str) -> Box<BoxableExpression<users::table, TestBackend, SqlType=types::Bool, SqlTypeForSelect=types::Bool>> {
        Box::new(lower(users::name).eq(name.to_string()))
    }

    let connection = connection_with_sean_and_tess_in_users_table();
    let sean = User::new(1, "Sean");
    let tess = User::new(2, "Tess");
    let queried_sean = users::table.filter(by_name("sean")).first(&connection);
    let queried_tess = users::table.filter(by_name("tess")).first(&connection);

    assert_eq!(Ok(sean), queried_sean);
    assert_eq!(Ok(tess), queried_tess);
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(missing_docs)]

pub enum Expression {
    IdentifierExpression {
        name: String,
    },
}

pub enum PatternElement {
    TextElement {
        value: String,
    },
    Placeable {
        expressions: Vec<Expression>,
    },
}

pub enum Value {
    Pattern {
        source: String,
        elements: Vec<PatternElement>,
    },
}

pub enum Entry {
    Entity {
        id: Identifier,
        value: Value,
    },
}

pub struct Identifier {
    pub name: String,
}
<commit_msg>Derive Debug for l20n AST nodes.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(missing_docs)]

#[derive(Debug)]
pub enum Expression {
    IdentifierExpression {
        name: String,
    },
}

#[derive(Debug)]
pub enum PatternElement {
    TextElement {
        value: String,
    },
    Placeable {
        expressions: Vec<Expression>,
    },
}

#[derive(Debug)]
pub enum Value {
    Pattern {
        source: String,
        elements: Vec<PatternElement>,
    },
}

#[derive(Debug)]
pub enum Entry {
    Entity {
        id: Identifier,
        value: Value,
    },
}

#[derive(Debug)]
pub struct Identifier {
    pub name: String,
}
<|endoftext|>


<commit_before>#[cfg(feature = "bindgen")]
extern crate bindgen;

#[cfg(feature = "pkg-config")]
extern crate pkg_config;

extern crate cc;
extern crate glob;

use std::path::PathBuf;
use std::{env, fs};

#[cfg(feature = "bindgen")]
fn generate_bindings(defs: Vec<&str>, headerpaths: Vec<PathBuf>) {
    let bindings = bindgen::Builder::default()
        .header("zstd.h")
        .blacklist_type("max_align_t")
        .use_core()
        .rustified_enum(".*")
        .clang_args(
            headerpaths
                .into_iter()
                .map(|path| format!("-I{}", path.display())),
        )
        .clang_args(defs.into_iter().map(|def| format!("-D{}", def)));

    #[cfg(feature = "experimental")]
    let bindings = bindings.clang_arg("-DZSTD_STATIC_LINKING_ONLY");

    #[cfg(not(feature = "std"))]
    let bindings = bindings.ctypes_prefix("libc");

    let bindings = bindings.generate().expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Could not write bindings");
}

#[cfg(not(feature = "bindgen"))]
fn generate_bindings(_: Vec<&str>, _: Vec<PathBuf>) {}

#[cfg(feature = "pkg-config")]
fn pkg_config() -> (Vec<&'static str>, Vec<PathBuf>) {
    let library = pkg_config::Config::new()
        .statik(true)
        .cargo_metadata(!cfg!(feature = "non-cargo"))
        .probe("libzstd")
        .expect("Can't probe for zstd in pkg-config");
    (vec!["PKG_CONFIG"], library.include_paths)
}

#[cfg(not(feature = "pkg-config"))]
fn pkg_config() -> (Vec<&'static str>, Vec<PathBuf>) {
    unimplemented!()
}

#[cfg(not(feature = "legacy"))]
fn set_legacy(_config: &mut cc::Build) {}

#[cfg(feature = "legacy")]
fn set_legacy(config: &mut cc::Build) {
    config.define("ZSTD_LEGACY_SUPPORT", Some("1"));
}

#[cfg(feature = "zstdmt")]
fn set_pthread(config: &mut cc::Build) {
    config.flag("-pthread");
}

#[cfg(not(feature = "zstdmt"))]
fn set_pthread(_config: &mut cc::Build) {}

fn compile_zstd() {
    let mut config = cc::Build::new();

    let globs = &[
        "zstd/lib/common/*.c",
        "zstd/lib/compress/*.c",
        "zstd/lib/decompress/*.c",
        "zstd/lib/legacy/*.c",
        "zstd/lib/dictBuilder/*.c",
    ];

    for pattern in globs {
        for path in glob::glob(pattern).unwrap() {
            let path = path.unwrap();
            config.file(path);
        }
    }

    // Some extra parameters
    config.opt_level(3);
    config.include("zstd/lib/");
    config.include("zstd/lib/common");
    config.include("zstd/lib/legacy");
    config.warnings(false);

    config.define("ZSTD_LIB_DEPRECATED", Some("0"));

    // Hide symbols from resulting library,
    // so we can be used with another zstd-linking lib.
    // See https://github.com/gyscos/zstd-rs/issues/58
    config.flag("-fvisibility=hidden");
    config.define("ZSTDLIB_VISIBILITY", Some(""));
    config.define("ZDICTLIB_VISIBILITY", Some(""));
    config.define("ZSTDERRORLIB_VISIBILITY", Some(""));

    set_pthread(&mut config);
    set_legacy(&mut config);

    // Compile!
    config.compile("libzstd.a");

    let src = env::current_dir().unwrap().join("zstd").join("lib");
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let include = dst.join("include");
    fs::create_dir_all(&include).unwrap();
    fs::copy(src.join("zstd.h"), include.join("zstd.h")).unwrap();
    fs::copy(
        src.join("dictBuilder").join("zdict.h"),
        include.join("zdict.h"),
    )
    .unwrap();
    println!("cargo:root={}", dst.display());
}

fn main() {
    // println!("cargo:rustc-link-lib=zstd");

    if !PathBuf::from("zstd/lib").exists() {
        panic!("Folder 'zstd/lib' does not exists. Maybe you forget clone 'zstd' submodule?");
    }

    let (defs, headerpaths) = if cfg!(feature = "pkg-config") {
        pkg_config()
    } else {
        if !PathBuf::from("zstd/lib").exists() {
            panic!("Folder 'zstd/lib' does not exists. Maybe you forget clone 'zstd' submodule?");
        }

        compile_zstd();
        (vec![], vec![PathBuf::from("zstd/lib")])
    };

    generate_bindings(defs, headerpaths);
}
<commit_msg>Copy public zstd_errors.h into output directory (#68)<commit_after>#[cfg(feature = "bindgen")]
extern crate bindgen;

#[cfg(feature = "pkg-config")]
extern crate pkg_config;

extern crate cc;
extern crate glob;

use std::path::PathBuf;
use std::{env, fs};

#[cfg(feature = "bindgen")]
fn generate_bindings(defs: Vec<&str>, headerpaths: Vec<PathBuf>) {
    let bindings = bindgen::Builder::default()
        .header("zstd.h")
        .blacklist_type("max_align_t")
        .use_core()
        .rustified_enum(".*")
        .clang_args(
            headerpaths
                .into_iter()
                .map(|path| format!("-I{}", path.display())),
        )
        .clang_args(defs.into_iter().map(|def| format!("-D{}", def)));

    #[cfg(feature = "experimental")]
    let bindings = bindings.clang_arg("-DZSTD_STATIC_LINKING_ONLY");

    #[cfg(not(feature = "std"))]
    let bindings = bindings.ctypes_prefix("libc");

    let bindings = bindings.generate().expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Could not write bindings");
}

#[cfg(not(feature = "bindgen"))]
fn generate_bindings(_: Vec<&str>, _: Vec<PathBuf>) {}

#[cfg(feature = "pkg-config")]
fn pkg_config() -> (Vec<&'static str>, Vec<PathBuf>) {
    let library = pkg_config::Config::new()
        .statik(true)
        .cargo_metadata(!cfg!(feature = "non-cargo"))
        .probe("libzstd")
        .expect("Can't probe for zstd in pkg-config");
    (vec!["PKG_CONFIG"], library.include_paths)
}

#[cfg(not(feature = "pkg-config"))]
fn pkg_config() -> (Vec<&'static str>, Vec<PathBuf>) {
    unimplemented!()
}

#[cfg(not(feature = "legacy"))]
fn set_legacy(_config: &mut cc::Build) {}

#[cfg(feature = "legacy")]
fn set_legacy(config: &mut cc::Build) {
    config.define("ZSTD_LEGACY_SUPPORT", Some("1"));
}

#[cfg(feature = "zstdmt")]
fn set_pthread(config: &mut cc::Build) {
    config.flag("-pthread");
}

#[cfg(not(feature = "zstdmt"))]
fn set_pthread(_config: &mut cc::Build) {}

fn compile_zstd() {
    let mut config = cc::Build::new();

    let globs = &[
        "zstd/lib/common/*.c",
        "zstd/lib/compress/*.c",
        "zstd/lib/decompress/*.c",
        "zstd/lib/legacy/*.c",
        "zstd/lib/dictBuilder/*.c",
    ];

    for pattern in globs {
        for path in glob::glob(pattern).unwrap() {
            let path = path.unwrap();
            config.file(path);
        }
    }

    // Some extra parameters
    config.opt_level(3);
    config.include("zstd/lib/");
    config.include("zstd/lib/common");
    config.include("zstd/lib/legacy");
    config.warnings(false);

    config.define("ZSTD_LIB_DEPRECATED", Some("0"));

    // Hide symbols from resulting library,
    // so we can be used with another zstd-linking lib.
    // See https://github.com/gyscos/zstd-rs/issues/58
    config.flag("-fvisibility=hidden");
    config.define("ZSTDLIB_VISIBILITY", Some(""));
    config.define("ZDICTLIB_VISIBILITY", Some(""));
    config.define("ZSTDERRORLIB_VISIBILITY", Some(""));

    set_pthread(&mut config);
    set_legacy(&mut config);

    // Compile!
    config.compile("libzstd.a");

    let src = env::current_dir().unwrap().join("zstd").join("lib");
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let include = dst.join("include");
    fs::create_dir_all(&include).unwrap();
    fs::copy(src.join("zstd.h"), include.join("zstd.h")).unwrap();
    fs::copy(
        src.join("common").join("zstd_errors.h"),
        include.join("zstd_errors.h"),
    )
    .unwrap();
    fs::copy(
        src.join("dictBuilder").join("zdict.h"),
        include.join("zdict.h"),
    )
    .unwrap();
    println!("cargo:root={}", dst.display());
}

fn main() {
    // println!("cargo:rustc-link-lib=zstd");

    if !PathBuf::from("zstd/lib").exists() {
        panic!("Folder 'zstd/lib' does not exists. Maybe you forget clone 'zstd' submodule?");
    }

    let (defs, headerpaths) = if cfg!(feature = "pkg-config") {
        pkg_config()
    } else {
        if !PathBuf::from("zstd/lib").exists() {
            panic!("Folder 'zstd/lib' does not exists. Maybe you forget clone 'zstd' submodule?");
        }

        compile_zstd();
        (vec![], vec![PathBuf::from("zstd/lib")])
    };

    generate_bindings(defs, headerpaths);
}
<|endoftext|>


<commit_before>use super::misc::ToFloat;
use std::ops::{Add, Sub, Div, Mul};

/// An iterator of a sequence of evenly spaced floats.
///
/// Iterator element type is `F`.
#[derive(Clone)]
pub struct Linspace<F> {
    start: F,
    step: F,
    index: usize,
    len: usize,
}

impl<F> Iterator for Linspace<F>
    where F: Copy + Add<Output=F> + Mul<Output=F>,
          usize: ToFloat<F>,
{
    type Item = F;

    #[inline]
    fn next(&mut self) -> Option<F> {
        if self.index >= self.len {
            None
        } else {
            // Calculate the value just like numpy.linspace does
            let i = self.index;
            self.index += 1;
            Some(self.start + self.step * i.to_float())
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let n = self.len - self.index;
        (n, Some(n))
    }
}

impl<F> DoubleEndedIterator for Linspace<F>
    where F: Copy + Add<Output=F> + Mul<Output=F>,
          usize: ToFloat<F>,
{
    #[inline]
    fn next_back(&mut self) -> Option<F> {
        if self.index >= self.len {
            None
        } else {
            // Calculate the value just like numpy.linspace does
            self.len -= 1;
            let i = self.len;
            Some(self.start + self.step * i.to_float())
        }
    }
}

impl<F> ExactSizeIterator for Linspace<F> where Linspace<F>: Iterator { }

/// Return an iterator of evenly spaced floats.
///
/// The `Linspace` has `n` elements, where the first
/// element is `a` and the last element is `b`.
///
/// Iterator element type is `F`, where `F` must be
/// either `f32` or `f64`.
///
/// ```
/// use itertools::linspace;
///
/// itertools::assert_equal(linspace::<f32>(0., 1., 5),
///                         vec![0., 0.25, 0.5, 0.75, 1.0]);
/// ```
#[inline]
pub fn linspace<F>(a: F, b: F, n: usize) -> Linspace<F> where
    F: Copy + Sub<Output=F> + Div<Output=F> + Mul<Output=F>,
    usize: ToFloat<F>,
{
    let step = if n > 1 {
        let nf: F = n.to_float();
        (b - a)/(nf - 1.to_float())
    } else {
        0.to_float()
    };
    Linspace {
        start: a,
        step: step,
        index: 0,
        len: n,
    }
}
<commit_msg>impl Debug for Linspace<commit_after>use super::misc::ToFloat;
use std::ops::{Add, Sub, Div, Mul};

/// An iterator of a sequence of evenly spaced floats.
///
/// Iterator element type is `F`.
#[derive(Clone, Debug)]
pub struct Linspace<F> {
    start: F,
    step: F,
    index: usize,
    len: usize,
}

impl<F> Iterator for Linspace<F>
    where F: Copy + Add<Output=F> + Mul<Output=F>,
          usize: ToFloat<F>,
{
    type Item = F;

    #[inline]
    fn next(&mut self) -> Option<F> {
        if self.index >= self.len {
            None
        } else {
            // Calculate the value just like numpy.linspace does
            let i = self.index;
            self.index += 1;
            Some(self.start + self.step * i.to_float())
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let n = self.len - self.index;
        (n, Some(n))
    }
}

impl<F> DoubleEndedIterator for Linspace<F>
    where F: Copy + Add<Output=F> + Mul<Output=F>,
          usize: ToFloat<F>,
{
    #[inline]
    fn next_back(&mut self) -> Option<F> {
        if self.index >= self.len {
            None
        } else {
            // Calculate the value just like numpy.linspace does
            self.len -= 1;
            let i = self.len;
            Some(self.start + self.step * i.to_float())
        }
    }
}

impl<F> ExactSizeIterator for Linspace<F> where Linspace<F>: Iterator { }

/// Return an iterator of evenly spaced floats.
///
/// The `Linspace` has `n` elements, where the first
/// element is `a` and the last element is `b`.
///
/// Iterator element type is `F`, where `F` must be
/// either `f32` or `f64`.
///
/// ```
/// use itertools::linspace;
///
/// itertools::assert_equal(linspace::<f32>(0., 1., 5),
///                         vec![0., 0.25, 0.5, 0.75, 1.0]);
/// ```
#[inline]
pub fn linspace<F>(a: F, b: F, n: usize) -> Linspace<F> where
    F: Copy + Sub<Output=F> + Div<Output=F> + Mul<Output=F>,
    usize: ToFloat<F>,
{
    let step = if n > 1 {
        let nf: F = n.to_float();
        (b - a)/(nf - 1.to_float())
    } else {
        0.to_float()
    };
    Linspace {
        start: a,
        step: step,
        index: 0,
        len: n,
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::mpsc::{TryRecvError, channel};
use std::sync::Arc;
use std::time::Duration;
use std::io;

use byteorder::{ByteOrder, LittleEndian};

use net::EventLoop;

#[test]
fn get_packet_length_correct() {
    let mut m_number: [u8; 4] = [0; 4];
    LittleEndian::write_u32(&mut m_number, super::NET_MAGIC_NUMBER);
    let mut intended_length: [u8; 2] = [0; 2];
    LittleEndian::write_u16(&mut intended_length, 10);
    let length = super::get_packet_length([m_number[0], m_number[1], m_number[2], m_number[3], intended_length[0], intended_length[1]]).expect("get_packet_length wrongly checks NET_MAGIC_NUMBER");
    assert_eq!(length, 10);
}

#[test]
fn get_packet_length_bad_magic_number() {
    let length: Option<u16> = super::get_packet_length([0xFE, 0xF0, 0xF6, 0xFD, 0, 10]);
    assert_eq!(length, None);
}

#[test]
fn handler_new() {
    super::Handler::new(super::MAX_CONNECTIONS);
}

#[test]
fn event_loop_impl_new() {
    super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
    unimplemented!()
}

#[test]
fn event_loop_impl_shutdown() {}
<commit_msg>Adds a unit test for shutting down an event loop<commit_after>use std::thread;
use std::sync::mpsc::{TryRecvError, channel};
use std::time::Duration;
use std::io;

use byteorder::{ByteOrder, LittleEndian};

use net::EventLoop;

#[test]
fn get_packet_length_correct() {
    let mut m_number: [u8; 4] = [0; 4];
    LittleEndian::write_u32(&mut m_number, super::NET_MAGIC_NUMBER);
    let mut intended_length: [u8; 2] = [0; 2];
    LittleEndian::write_u16(&mut intended_length, 10);
    let length = super::get_packet_length([m_number[0],
                                           m_number[1],
                                           m_number[2],
                                           m_number[3],
                                           intended_length[0],
                                           intended_length[1]])
                     .expect("get_packet_length wrongly checks NET_MAGIC_NUMBER");
    assert_eq!(length, 10);
}

#[test]
fn get_packet_length_bad_magic_number() {
    let length: Option<u16> = super::get_packet_length([0xFE, 0xF0, 0xF6, 0xFD, 0, 10]);
    assert_eq!(length, None);
}

#[test]
fn handler_new() {
    super::Handler::new(super::MAX_CONNECTIONS);
}

#[test]
fn event_loop_impl_new() {
    super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
}

#[test]
fn event_loop_impl_shutdown() {
    let (tx, rx) = channel::<Result<(), io::Error>>();
    let mut event_loop = super::EventLoopImpl::new(super::MAX_CONNECTIONS).unwrap();
    let event_loop_ref: super::EventLoopImplRef = (&mut event_loop).into();
    let thread = thread::spawn(move || tx.send(event_loop.run()).unwrap());
    event_loop_ref.shutdown();
    thread::sleep(Duration::from_millis(250));
    // This may, once in a very long time, fail. It really shouldn't, but it is possible.
    // Just raise the number or try again if it fails.
    match rx.try_recv() {
        Err(TryRecvError::Empty) => panic!("EventLoopImpl did not stop after calling shutdown()"),
        Err(TryRecvError::Disconnected) => {
            panic!("EventLoop somehow disconnected it's channel without stopping")
        }
        Ok(res) => res,
    }
    .unwrap();
    thread.join().unwrap();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use super::{Drawable, BoundingBox, MeasureMode};
use ::paint::{Canvas, Point, Rect};
use ::platform::Context;

pub type PaddingReader<T> = fn (&T) -> f32;

pub struct PaddingBox<'a, T: 'a, U: Drawable> {
    wrapped: Option<U>,
    props: &'a T,
    padding_left_reader: PaddingReader<T>,
    padding_right_reader: PaddingReader<T>,
    padding_top_reader: PaddingReader<T>,
    padding_bottom_reader: PaddingReader<T>,

    bounding_box: BoundingBox
}

impl<'a, T, U: Drawable> Drawable for PaddingBox<'a, T, U> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        if let Some(ref wrapped) = self.wrapped {
            wrapped.draw(canvas, pen_pos);
        }
    }

    fn calculate(&mut self, context: &Context, width: f32, width_mode: &MeasureMode, height: f32,
                 height_mode: &MeasureMode) {
        let padding_left = (self.padding_left_reader)(self.props);
        let padding_right = (self.padding_right_reader)(self.props);
        let padding_top = (self.padding_top_reader)(self.props);
        let padding_bottom = (self.padding_bottom_reader)(self.props);

        self.bounding_box = if let Some(ref mut val) = self.wrapped {
            val.calculate(context, width, width_mode, height, height_mode);
            BoundingBox {
                rect: Rect::new(val.bounding_box().width()+padding_left+padding_right,
                                val.bounding_box().height()+padding_top+padding_bottom),
                baseline: val.bounding_box().baseline()+padding_bottom,
                axis: val.bounding_box().axis()+padding_bottom,
            }
        } else {
            BoundingBox {
                rect: Rect::new(padding_left+padding_right, padding_top+padding_bottom),
                baseline: 0.0,
                axis: 0.0,
            }
        }
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T, U: Drawable> PaddingBox<'a, T, U> {
    pub fn new(props: &'a T, padding_left_reader: PaddingReader<T>, padding_right_reader: PaddingReader<T>,
               padding_top_reader: PaddingReader<T>, padding_bottom_reader: PaddingReader<T>)
               -> PaddingBox<'a, T, U> {
        PaddingBox {
            wrapped: None,
            props,
            padding_left_reader,
            padding_right_reader,
            padding_top_reader,
            padding_bottom_reader,
            bounding_box: BoundingBox::default(),
        }
    }

    pub fn wrap(&mut self, drawable: U) {
        self.wrapped = Some(drawable);
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::{Fixed, MeasureMode};
    use ::platform::test::test_context;

    struct PaddingProps;

    #[test]
    fn it_pads() {
        let content = Fixed::new(10., 20., 2., 5.);
        let props = PaddingProps {};
        let context = test_context();
        let mut wrapped = PaddingBox::new(
            &props,
            |_| 5.,
            |_| 3.,
            |_| 4.,
            |_| 2.
        );

        wrapped.calculate(&context, -1., &MeasureMode::Wrap,
                          -1., &MeasureMode::Wrap);

        assert_eq!(wrapped.bounding_box().width(), 8.);
        assert_eq!(wrapped.bounding_box().height(), 6.);
        assert_eq!(wrapped.bounding_box().baseline(), 0.);
        assert_eq!(wrapped.bounding_box().axis(), 0.);

        wrapped.wrap(content);
        wrapped.calculate(&context, -1., &MeasureMode::Wrap,
                          -1., &MeasureMode::Wrap);

        assert_eq!(wrapped.bounding_box().width(), 18.);
        assert_eq!(wrapped.bounding_box().height(), 26.);
        assert_eq!(wrapped.bounding_box().baseline(), 4.);
        assert_eq!(wrapped.bounding_box().axis(), 7.);
    }
}<commit_msg>Fix improper position of wrapped content<commit_after>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use super::{Drawable, BoundingBox, MeasureMode};
use ::paint::{Canvas, Point, Rect};
use ::platform::Context;

pub type PaddingReader<T> = fn (&T) -> f32;

pub struct PaddingBox<'a, T: 'a, U: Drawable> {
    wrapped: Option<U>,
    props: &'a T,
    padding_left_reader: PaddingReader<T>,
    padding_right_reader: PaddingReader<T>,
    padding_top_reader: PaddingReader<T>,
    padding_bottom_reader: PaddingReader<T>,

    bounding_box: BoundingBox
}

impl<'a, T, U: Drawable> Drawable for PaddingBox<'a, T, U> {
    fn draw(&self, canvas: &Canvas, pen_pos: &Point) {
        if let Some(ref wrapped) = self.wrapped {
            let point = pen_pos+&Point::new((self.padding_left_reader)(self.props),
                                            (self.padding_top_reader)(self.props));
            wrapped.draw(canvas, &point);
        }
    }

    fn calculate(&mut self, context: &Context, width: f32, width_mode: &MeasureMode, height: f32,
                 height_mode: &MeasureMode) {
        let padding_left = (self.padding_left_reader)(self.props);
        let padding_right = (self.padding_right_reader)(self.props);
        let padding_top = (self.padding_top_reader)(self.props);
        let padding_bottom = (self.padding_bottom_reader)(self.props);

        self.bounding_box = if let Some(ref mut val) = self.wrapped {
            val.calculate(context, width, width_mode, height, height_mode);
            BoundingBox {
                rect: Rect::new(val.bounding_box().width()+padding_left+padding_right,
                                val.bounding_box().height()+padding_top+padding_bottom),
                baseline: val.bounding_box().baseline()+padding_bottom,
                axis: val.bounding_box().axis()+padding_bottom,
            }
        } else {
            BoundingBox {
                rect: Rect::new(padding_left+padding_right, padding_top+padding_bottom),
                baseline: 0.0,
                axis: 0.0,
            }
        }
    }

    fn bounding_box(&self) -> &BoundingBox {
        &self.bounding_box
    }
}

impl<'a, T, U: Drawable> PaddingBox<'a, T, U> {
    pub fn new(props: &'a T, padding_left_reader: PaddingReader<T>, padding_right_reader: PaddingReader<T>,
               padding_top_reader: PaddingReader<T>, padding_bottom_reader: PaddingReader<T>)
               -> PaddingBox<'a, T, U> {
        PaddingBox {
            wrapped: None,
            props,
            padding_left_reader,
            padding_right_reader,
            padding_top_reader,
            padding_bottom_reader,
            bounding_box: BoundingBox::default(),
        }
    }

    pub fn wrap(&mut self, drawable: U) {
        self.wrapped = Some(drawable);
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::{Fixed, MeasureMode};
    use ::platform::test::test_context;

    struct PaddingProps;

    #[test]
    fn it_pads() {
        let content = Fixed::new(10., 20., 2., 5.);
        let props = PaddingProps {};
        let context = test_context();
        let mut wrapped = PaddingBox::new(
            &props,
            |_| 5.,
            |_| 3.,
            |_| 4.,
            |_| 2.
        );

        wrapped.calculate(&context, -1., &MeasureMode::Wrap,
                          -1., &MeasureMode::Wrap);

        assert_eq!(wrapped.bounding_box().width(), 8.);
        assert_eq!(wrapped.bounding_box().height(), 6.);
        assert_eq!(wrapped.bounding_box().baseline(), 0.);
        assert_eq!(wrapped.bounding_box().axis(), 0.);

        wrapped.wrap(content);
        wrapped.calculate(&context, -1., &MeasureMode::Wrap,
                          -1., &MeasureMode::Wrap);

        assert_eq!(wrapped.bounding_box().width(), 18.);
        assert_eq!(wrapped.bounding_box().height(), 26.);
        assert_eq!(wrapped.bounding_box().baseline(), 4.);
        assert_eq!(wrapped.bounding_box().axis(), 7.);
    }
}<|endoftext|>


<commit_before>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::error;
use std::fmt;
use std::io::Write;

use super::super::value::{Float, Integer, ValueRef};
use super::FixedValueWriteError;
use super::ValueWriteError;
use super::WriteError;
use super::write_array_len;
use super::write_bin;
use super::write_bool;
use super::write_ext_meta;
use super::write_f32;
use super::write_f64;
use super::write_map_len;
use super::write_nil;
use super::write_sint;
use super::write_str;
use super::write_uint;

#[derive(Debug)]
pub struct Error(WriteError);

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error(ref err) => err.fmt(fmt),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error(ref err) => err.cause(),
        }
    }
}

impl From<FixedValueWriteError> for Error {
    fn from(err: FixedValueWriteError) -> Error {
        match err {
            FixedValueWriteError(err) => Error(err)
        }
    }
}

impl From<ValueWriteError> for Error {
    fn from(err: ValueWriteError) -> Error {
        match err {
            ValueWriteError::InvalidMarkerWrite(err) => Error(err),
            ValueWriteError::InvalidDataWrite(err) => Error(err)
        }
    }
}

pub fn write_value_ref<W>(wr: &mut W, val: &ValueRef) -> Result<(), Error>
    where W: Write
{
    match val {
        &ValueRef::Nil => try!(write_nil(wr)),
        &ValueRef::Boolean(val) => try!(write_bool(wr, val)),
        &ValueRef::Integer(Integer::U64(val)) => {
            try!(write_uint(wr, val));
        }
        &ValueRef::Integer(Integer::I64(val)) => {
            try!(write_sint(wr, val));
        }
        // TODO: Replace with generic write_float(...).
        &ValueRef::Float(Float::F32(val)) => try!(write_f32(wr, val)),
        &ValueRef::Float(Float::F64(val)) => try!(write_f64(wr, val)),
        &ValueRef::String(val) => {
            try!(write_str(wr, val));
        }
        &ValueRef::Binary(val) => {
            try!(write_bin(wr, val));
        }
        &ValueRef::Array(ref val) => {
            let len = val.len() as u32;

            try!(write_array_len(wr, len));

            for item in val {
                try!(write_value_ref(wr, item));
            }
        }
        &ValueRef::Map(ref val) => {
            let len = val.len() as u32;

            try!(write_map_len(wr, len));

            for &(ref key, ref val) in val {
                try!(write_value_ref(wr, key));
                try!(write_value_ref(wr, val));
            }
        }
        &ValueRef::Ext(ty, data) => {
            try!(write_ext_meta(wr, data.len() as u32, ty));
            try!(wr.write_all(data).map_err(|err| ValueWriteError::InvalidDataWrite(WriteError(err))));
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use std::error;
    use std::io;
    use super::super::WriteError;
    use super::Error;

    #[test]
    fn display_trait() {
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        assert_eq!("err: error while writing MessagePack'ed value", format!("err: {}", err));
    }

    #[test]
    fn error_trait() {
        // Delegates to I/O Error.
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        fn test<E: error::Error>(err: E) {
            assert_eq!("error while writing MessagePack'ed value", err.description());
            assert!(err.cause().is_some());
            assert_eq!("unexpected EOF", err.cause().unwrap().description());
        }

        test(err);
    }
}
<commit_msg>ValueRef: documentation added.<commit_after>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::error;
use std::fmt;
use std::io::Write;

use super::super::value::{Float, Integer, ValueRef};
use super::FixedValueWriteError;
use super::ValueWriteError;
use super::WriteError;
use super::write_array_len;
use super::write_bin;
use super::write_bool;
use super::write_ext_meta;
use super::write_f32;
use super::write_f64;
use super::write_map_len;
use super::write_nil;
use super::write_sint;
use super::write_str;
use super::write_uint;

#[derive(Debug)]
pub struct Error(WriteError);

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error(ref err) => err.fmt(fmt),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error(ref err) => err.cause(),
        }
    }
}

impl From<FixedValueWriteError> for Error {
    fn from(err: FixedValueWriteError) -> Error {
        match err {
            FixedValueWriteError(err) => Error(err)
        }
    }
}

impl From<ValueWriteError> for Error {
    fn from(err: ValueWriteError) -> Error {
        match err {
            ValueWriteError::InvalidMarkerWrite(err) => Error(err),
            ValueWriteError::InvalidDataWrite(err) => Error(err)
        }
    }
}

/// Encodes and attempts to write the given non-owning ValueRef into the given Write.
///
/// # Errors
///
/// This function returns Error with an underlying I/O error if unable to properly write entire
/// value. Interruption errors are handled internally by silent operation restarting.
///
/// # Examples
/// ```
/// use rmp::ValueRef;
/// use rmp::encode::value_ref::write_value_ref;
///
/// let mut buf = Vec::new();
/// let val = ValueRef::String("le message");
///
/// write_value_ref(&mut buf, &val).unwrap();
/// assert_eq!(vec![0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf);
/// ```
pub fn write_value_ref<W>(wr: &mut W, val: &ValueRef) -> Result<(), Error>
    where W: Write
{
    match val {
        &ValueRef::Nil => try!(write_nil(wr)),
        &ValueRef::Boolean(val) => try!(write_bool(wr, val)),
        &ValueRef::Integer(Integer::U64(val)) => {
            try!(write_uint(wr, val));
        }
        &ValueRef::Integer(Integer::I64(val)) => {
            try!(write_sint(wr, val));
        }
        // TODO: Replace with generic write_float(...).
        &ValueRef::Float(Float::F32(val)) => try!(write_f32(wr, val)),
        &ValueRef::Float(Float::F64(val)) => try!(write_f64(wr, val)),
        &ValueRef::String(val) => {
            try!(write_str(wr, val));
        }
        &ValueRef::Binary(val) => {
            try!(write_bin(wr, val));
        }
        &ValueRef::Array(ref val) => {
            let len = val.len() as u32;

            try!(write_array_len(wr, len));

            for item in val {
                try!(write_value_ref(wr, item));
            }
        }
        &ValueRef::Map(ref val) => {
            let len = val.len() as u32;

            try!(write_map_len(wr, len));

            for &(ref key, ref val) in val {
                try!(write_value_ref(wr, key));
                try!(write_value_ref(wr, val));
            }
        }
        &ValueRef::Ext(ty, data) => {
            try!(write_ext_meta(wr, data.len() as u32, ty));
            try!(wr.write_all(data).map_err(|err| ValueWriteError::InvalidDataWrite(WriteError(err))));
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use std::error;
    use std::io;
    use super::super::WriteError;
    use super::Error;

    #[test]
    fn display_trait() {
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        assert_eq!("err: error while writing MessagePack'ed value", format!("err: {}", err));
    }

    #[test]
    fn error_trait() {
        // Delegates to I/O Error.
        let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF")));

        fn test<E: error::Error>(err: E) {
            assert_eq!("error while writing MessagePack'ed value", err.description());
            assert!(err.cause().is_some());
            assert_eq!("unexpected EOF", err.cause().unwrap().description());
        }

        test(err);
    }
}
<|endoftext|>


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

use std::{
    borrow::Borrow,
    collections::HashMap,
    convert::TryFrom,
    ffi::OsStr,
    fmt::{self, Debug, Display},
    hash::Hash,
    ops::Deref,
    path::{Path, PathBuf},
    sync::Arc,
};

use libudev::EventType;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::sync::Mutex;
use uuid::Uuid;

use devicemapper::Bytes;

pub use crate::engine::{
    engine::{Engine, StateDiff},
    structures::Lockable,
    types::{
        actions::{
            Clevis, CreateAction, DeleteAction, EngineAction, Key, MappingCreateAction,
            MappingDeleteAction, RegenAction, RenameAction, SetCreateAction, SetDeleteAction,
            SetUnlockAction,
        },
        keys::{EncryptionInfo, KeyDescription, PoolEncryptionInfo, SizedKeyMemory},
    },
};
use crate::stratis::{StratisError, StratisResult};

mod actions;
mod keys;

macro_rules! uuid {
    ($vis:vis $ident:ident) => {
        #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Deserialize, Serialize)]
        $vis struct $ident(pub uuid::Uuid);

        impl $ident {
            pub fn new_v4() -> Self {
                $ident(uuid::Uuid::new_v4())
            }

            pub fn parse_str(s: &str) -> $crate::stratis::StratisResult<Self> {
                Ok($ident(uuid::Uuid::parse_str(s)?))
            }

            pub fn nil() -> Self {
                $ident(uuid::Uuid::nil())
            }
        }

        impl std::ops::Deref for $ident {
            type Target = uuid::Uuid;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl std::fmt::Display for $ident {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                std::fmt::Display::fmt(&self.0, f)
            }
        }

        impl $crate::engine::types::AsUuid for $ident {}
    }
}

/// Value representing Clevis config information.
pub type ClevisInfo = (String, Value);

/// An engine that can be locked for synchronization.
pub type LockableEngine<E> = Lockable<Arc<Mutex<E>>>;

pub trait AsUuid:
    Copy
    + Clone
    + Debug
    + Hash
    + Eq
    + PartialEq
    + for<'a> Deserialize<'a>
    + Serialize
    + Deref<Target = Uuid>
    + Display
{
}

uuid!(pub DevUuid);

uuid!(pub FilesystemUuid);

uuid!(pub PoolUuid);

#[derive(Debug)]
pub enum StratisUuid {
    Dev(DevUuid),
    Fs(FilesystemUuid),
    Pool(PoolUuid),
}

impl Deref for StratisUuid {
    type Target = Uuid;

    fn deref(&self) -> &Self::Target {
        match self {
            StratisUuid::Dev(d) => &*d,
            StratisUuid::Fs(f) => &*f,
            StratisUuid::Pool(p) => &*p,
        }
    }
}

impl Display for StratisUuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            StratisUuid::Dev(d) => Display::fmt(d, f),
            StratisUuid::Fs(fs) => Display::fmt(fs, f),
            StratisUuid::Pool(p) => Display::fmt(p, f),
        }
    }
}

/// Use Clevis or keyring to unlock LUKS volume.
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum UnlockMethod {
    Clevis,
    Keyring,
}

impl<'a> TryFrom<&'a str> for UnlockMethod {
    type Error = StratisError;

    fn try_from(s: &str) -> StratisResult<UnlockMethod> {
        match s {
            "keyring" => Ok(UnlockMethod::Keyring),
            "clevis" => Ok(UnlockMethod::Clevis),
            _ => Err(StratisError::Msg(format!(
                "{} is an invalid unlock method",
                s
            ))),
        }
    }
}

/// Blockdev tier. Used to distinguish between blockdevs used for
/// data and blockdevs used for a cache.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockDevTier {
    Data = 0,
    Cache = 1,
}

/// Redundancy classifications which the engine allows for pools.
#[derive(Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
/// Redundancy specification for a pool.
pub enum Redundancy {
    NONE = 0,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Name(String);

impl Name {
    pub fn new(name: String) -> Name {
        Name(name)
    }

    pub fn to_owned(&self) -> String {
        self.0.clone()
    }
}

impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Deref for Name {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

impl Borrow<str> for Name {
    fn borrow(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// The type of report for which to query.
///
/// NOTE: `EngineState` is no longer an option and is now supported in the Manager D-Bus API.
/// This report may be fetched through `Report::engine_state_report()`.
///
/// * `ErroredPoolDevices` returns the state of devices that caused an error while
/// attempting to reconstruct a pool.
pub enum ReportType {
    ErroredPoolDevices,
}

impl<'a> TryFrom<&'a str> for ReportType {
    type Error = StratisError;

    fn try_from(name: &str) -> StratisResult<ReportType> {
        match name {
            "errored_pool_report" => Ok(ReportType::ErroredPoolDevices),
            _ => Err(StratisError::Msg(format!(
                "Report name {} not understood",
                name
            ))),
        }
    }
}

pub struct LockedPoolDevice {
    pub devnode: PathBuf,
    pub uuid: DevUuid,
}

pub struct LockedPoolInfo {
    pub info: PoolEncryptionInfo,
    pub devices: Vec<LockedPoolDevice>,
}

/// A sendable event with all of the necessary information for the engine
/// to process a received udev event.
pub struct UdevEngineEvent {
    event_type: EventType,
    device: UdevEngineDevice,
}

impl UdevEngineEvent {
    pub fn event_type(&self) -> EventType {
        self.event_type
    }

    pub fn device(&self) -> &UdevEngineDevice {
        &self.device
    }
}

impl<'a> From<&'a libudev::Event<'a>> for UdevEngineEvent {
    fn from(e: &'a libudev::Event<'a>) -> UdevEngineEvent {
        UdevEngineEvent {
            event_type: e.event_type(),
            device: UdevEngineDevice::from(e.device()),
        }
    }
}

/// A sendable device with all of the necessary information for the engine
/// to process a received udev event.
pub struct UdevEngineDevice {
    is_initialized: bool,
    devnode: Option<PathBuf>,
    devnum: Option<libc::dev_t>,
    properties: HashMap<Box<OsStr>, Box<OsStr>>,
}

impl UdevEngineDevice {
    pub fn is_initialized(&self) -> bool {
        self.is_initialized
    }

    pub fn devnode(&self) -> Option<&Path> {
        self.devnode.as_deref()
    }

    pub fn devnum(&self) -> Option<libc::dev_t> {
        self.devnum
    }

    pub fn property_value<T>(&self, property_name: T) -> Option<&OsStr>
    where
        T: AsRef<OsStr>,
    {
        self.properties
            .get(property_name.as_ref())
            .map(|prop| &**prop)
    }
}

impl<'a> From<&'a libudev::Device<'a>> for UdevEngineDevice {
    fn from(d: &'a libudev::Device<'a>) -> UdevEngineDevice {
        UdevEngineDevice {
            is_initialized: d.is_initialized(),
            devnode: d.devnode().map(|p| p.to_owned()),
            devnum: d.devnum(),
            properties: d
                .properties()
                .map(|prop| (Box::from(prop.name()), Box::from(prop.value())))
                .collect(),
        }
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DevicePath(PathBuf);

impl DevicePath {
    pub fn new(path: &Path) -> StratisResult<Self> {
        Ok(DevicePath(path.canonicalize()?))
    }
}

impl Deref for DevicePath {
    type Target = Path;

    fn deref(&self) -> &Self::Target {
        self.0.as_path()
    }
}

/// Represents what actions this pool can accept.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub enum ActionAvailability {
    /// Full set of actions may be taken
    Full = 0,
    /// No requests via an IPC mechanism may be taken
    NoRequests = 1,
    /// No changes may be made to the pool including background changes
    /// like reacting to devicemapper events
    NoPoolChanges = 2,
    /// The pool should not accept
    ReadOnly = 3,
}

impl Display for ActionAvailability {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                ActionAvailability::Full => "fully_operational",
                ActionAvailability::NoRequests => "no_ipc_requests",
                ActionAvailability::NoPoolChanges => "no_pool_changes",
                ActionAvailability::ReadOnly => "no_write_io",
            }
        )
    }
}

/// Indicates that a property that should be consistent across block devices
/// in a pool may be inconsistent.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum MaybeInconsistent<T> {
    Yes,
    No(T),
}

/// A set of properties that can change independently of IPC calls.
#[derive(Default)]
pub struct ChangedProperties {
    // NOTE: Bytes will eventually become a more general structure if more information
    // needs to be sent to the D-Bus layer.
    pub filesystem_props: HashMap<FilesystemUuid, Bytes>,
    // NOTE: Any additional properties should be specified similarly (HashMap between
    // UUIDs and a set of properties that have changed.
}

impl ChangedProperties {
    /// Returns a boolean indicating whether any properties were changed.
    pub fn is_changed(&self) -> bool {
        !self.filesystem_props.is_empty()
    }

    /// Add a new record for filesystem properties that have changed.
    pub fn add_fs_prop(&mut self, uuid: FilesystemUuid, diff: StratFilesystemDiff) {
        if let Some(bytes) = diff.0 {
            self.filesystem_props.insert(uuid, bytes);
        }
    }

    /// Merge two ChangedProperties structs.
    pub fn merge(&mut self, other: Self) -> &mut Self {
        self.filesystem_props.extend(other.filesystem_props);
        self
    }
}

/// Represents the difference between two dumped states for a filesystem.
#[derive(Default)]
pub struct StratFilesystemDiff(Option<Bytes>);

/// Represents the state of the Stratis filesystem at a given moment in time.
pub struct StratFilesystemState(Bytes);

impl StratFilesystemState {
    pub fn new(size: Bytes) -> Self {
        StratFilesystemState(size)
    }
}

impl StateDiff for StratFilesystemState {
    type Diff = StratFilesystemDiff;

    fn diff(&self, new_state: &Self) -> Self::Diff {
        StratFilesystemDiff(if self.0 != new_state.0 {
            Some(new_state.0)
        } else {
            None
        })
    }
}
<commit_msg>Remove unfeasible ReadOnly action availability mode<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{
    borrow::Borrow,
    collections::HashMap,
    convert::TryFrom,
    ffi::OsStr,
    fmt::{self, Debug, Display},
    hash::Hash,
    ops::Deref,
    path::{Path, PathBuf},
    sync::Arc,
};

use libudev::EventType;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::sync::Mutex;
use uuid::Uuid;

use devicemapper::Bytes;

pub use crate::engine::{
    engine::{Engine, StateDiff},
    structures::Lockable,
    types::{
        actions::{
            Clevis, CreateAction, DeleteAction, EngineAction, Key, MappingCreateAction,
            MappingDeleteAction, RegenAction, RenameAction, SetCreateAction, SetDeleteAction,
            SetUnlockAction,
        },
        keys::{EncryptionInfo, KeyDescription, PoolEncryptionInfo, SizedKeyMemory},
    },
};
use crate::stratis::{StratisError, StratisResult};

mod actions;
mod keys;

macro_rules! uuid {
    ($vis:vis $ident:ident) => {
        #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Deserialize, Serialize)]
        $vis struct $ident(pub uuid::Uuid);

        impl $ident {
            pub fn new_v4() -> Self {
                $ident(uuid::Uuid::new_v4())
            }

            pub fn parse_str(s: &str) -> $crate::stratis::StratisResult<Self> {
                Ok($ident(uuid::Uuid::parse_str(s)?))
            }

            pub fn nil() -> Self {
                $ident(uuid::Uuid::nil())
            }
        }

        impl std::ops::Deref for $ident {
            type Target = uuid::Uuid;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl std::fmt::Display for $ident {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                std::fmt::Display::fmt(&self.0, f)
            }
        }

        impl $crate::engine::types::AsUuid for $ident {}
    }
}

/// Value representing Clevis config information.
pub type ClevisInfo = (String, Value);

/// An engine that can be locked for synchronization.
pub type LockableEngine<E> = Lockable<Arc<Mutex<E>>>;

pub trait AsUuid:
    Copy
    + Clone
    + Debug
    + Hash
    + Eq
    + PartialEq
    + for<'a> Deserialize<'a>
    + Serialize
    + Deref<Target = Uuid>
    + Display
{
}

uuid!(pub DevUuid);

uuid!(pub FilesystemUuid);

uuid!(pub PoolUuid);

#[derive(Debug)]
pub enum StratisUuid {
    Dev(DevUuid),
    Fs(FilesystemUuid),
    Pool(PoolUuid),
}

impl Deref for StratisUuid {
    type Target = Uuid;

    fn deref(&self) -> &Self::Target {
        match self {
            StratisUuid::Dev(d) => &*d,
            StratisUuid::Fs(f) => &*f,
            StratisUuid::Pool(p) => &*p,
        }
    }
}

impl Display for StratisUuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            StratisUuid::Dev(d) => Display::fmt(d, f),
            StratisUuid::Fs(fs) => Display::fmt(fs, f),
            StratisUuid::Pool(p) => Display::fmt(p, f),
        }
    }
}

/// Use Clevis or keyring to unlock LUKS volume.
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum UnlockMethod {
    Clevis,
    Keyring,
}

impl<'a> TryFrom<&'a str> for UnlockMethod {
    type Error = StratisError;

    fn try_from(s: &str) -> StratisResult<UnlockMethod> {
        match s {
            "keyring" => Ok(UnlockMethod::Keyring),
            "clevis" => Ok(UnlockMethod::Clevis),
            _ => Err(StratisError::Msg(format!(
                "{} is an invalid unlock method",
                s
            ))),
        }
    }
}

/// Blockdev tier. Used to distinguish between blockdevs used for
/// data and blockdevs used for a cache.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockDevTier {
    Data = 0,
    Cache = 1,
}

/// Redundancy classifications which the engine allows for pools.
#[derive(Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
/// Redundancy specification for a pool.
pub enum Redundancy {
    NONE = 0,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Name(String);

impl Name {
    pub fn new(name: String) -> Name {
        Name(name)
    }

    pub fn to_owned(&self) -> String {
        self.0.clone()
    }
}

impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Deref for Name {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

impl Borrow<str> for Name {
    fn borrow(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// The type of report for which to query.
///
/// NOTE: `EngineState` is no longer an option and is now supported in the Manager D-Bus API.
/// This report may be fetched through `Report::engine_state_report()`.
///
/// * `ErroredPoolDevices` returns the state of devices that caused an error while
/// attempting to reconstruct a pool.
pub enum ReportType {
    ErroredPoolDevices,
}

impl<'a> TryFrom<&'a str> for ReportType {
    type Error = StratisError;

    fn try_from(name: &str) -> StratisResult<ReportType> {
        match name {
            "errored_pool_report" => Ok(ReportType::ErroredPoolDevices),
            _ => Err(StratisError::Msg(format!(
                "Report name {} not understood",
                name
            ))),
        }
    }
}

pub struct LockedPoolDevice {
    pub devnode: PathBuf,
    pub uuid: DevUuid,
}

pub struct LockedPoolInfo {
    pub info: PoolEncryptionInfo,
    pub devices: Vec<LockedPoolDevice>,
}

/// A sendable event with all of the necessary information for the engine
/// to process a received udev event.
pub struct UdevEngineEvent {
    event_type: EventType,
    device: UdevEngineDevice,
}

impl UdevEngineEvent {
    pub fn event_type(&self) -> EventType {
        self.event_type
    }

    pub fn device(&self) -> &UdevEngineDevice {
        &self.device
    }
}

impl<'a> From<&'a libudev::Event<'a>> for UdevEngineEvent {
    fn from(e: &'a libudev::Event<'a>) -> UdevEngineEvent {
        UdevEngineEvent {
            event_type: e.event_type(),
            device: UdevEngineDevice::from(e.device()),
        }
    }
}

/// A sendable device with all of the necessary information for the engine
/// to process a received udev event.
pub struct UdevEngineDevice {
    is_initialized: bool,
    devnode: Option<PathBuf>,
    devnum: Option<libc::dev_t>,
    properties: HashMap<Box<OsStr>, Box<OsStr>>,
}

impl UdevEngineDevice {
    pub fn is_initialized(&self) -> bool {
        self.is_initialized
    }

    pub fn devnode(&self) -> Option<&Path> {
        self.devnode.as_deref()
    }

    pub fn devnum(&self) -> Option<libc::dev_t> {
        self.devnum
    }

    pub fn property_value<T>(&self, property_name: T) -> Option<&OsStr>
    where
        T: AsRef<OsStr>,
    {
        self.properties
            .get(property_name.as_ref())
            .map(|prop| &**prop)
    }
}

impl<'a> From<&'a libudev::Device<'a>> for UdevEngineDevice {
    fn from(d: &'a libudev::Device<'a>) -> UdevEngineDevice {
        UdevEngineDevice {
            is_initialized: d.is_initialized(),
            devnode: d.devnode().map(|p| p.to_owned()),
            devnum: d.devnum(),
            properties: d
                .properties()
                .map(|prop| (Box::from(prop.name()), Box::from(prop.value())))
                .collect(),
        }
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DevicePath(PathBuf);

impl DevicePath {
    pub fn new(path: &Path) -> StratisResult<Self> {
        Ok(DevicePath(path.canonicalize()?))
    }
}

impl Deref for DevicePath {
    type Target = Path;

    fn deref(&self) -> &Self::Target {
        self.0.as_path()
    }
}

/// Represents what actions this pool can accept.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub enum ActionAvailability {
    /// Full set of actions may be taken
    Full = 0,
    /// No requests via an IPC mechanism may be taken
    NoRequests = 1,
    /// No changes may be made to the pool including background changes
    /// like reacting to devicemapper events
    NoPoolChanges = 2,
}

impl Display for ActionAvailability {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                ActionAvailability::Full => "fully_operational",
                ActionAvailability::NoRequests => "no_ipc_requests",
                ActionAvailability::NoPoolChanges => "no_pool_changes",
            }
        )
    }
}

/// Indicates that a property that should be consistent across block devices
/// in a pool may be inconsistent.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum MaybeInconsistent<T> {
    Yes,
    No(T),
}

/// A set of properties that can change independently of IPC calls.
#[derive(Default)]
pub struct ChangedProperties {
    // NOTE: Bytes will eventually become a more general structure if more information
    // needs to be sent to the D-Bus layer.
    pub filesystem_props: HashMap<FilesystemUuid, Bytes>,
    // NOTE: Any additional properties should be specified similarly (HashMap between
    // UUIDs and a set of properties that have changed.
}

impl ChangedProperties {
    /// Returns a boolean indicating whether any properties were changed.
    pub fn is_changed(&self) -> bool {
        !self.filesystem_props.is_empty()
    }

    /// Add a new record for filesystem properties that have changed.
    pub fn add_fs_prop(&mut self, uuid: FilesystemUuid, diff: StratFilesystemDiff) {
        if let Some(bytes) = diff.0 {
            self.filesystem_props.insert(uuid, bytes);
        }
    }

    /// Merge two ChangedProperties structs.
    pub fn merge(&mut self, other: Self) -> &mut Self {
        self.filesystem_props.extend(other.filesystem_props);
        self
    }
}

/// Represents the difference between two dumped states for a filesystem.
#[derive(Default)]
pub struct StratFilesystemDiff(Option<Bytes>);

/// Represents the state of the Stratis filesystem at a given moment in time.
pub struct StratFilesystemState(Bytes);

impl StratFilesystemState {
    pub fn new(size: Bytes) -> Self {
        StratFilesystemState(size)
    }
}

impl StateDiff for StratFilesystemState {
    type Diff = StratFilesystemDiff;

    fn diff(&self, new_state: &Self) -> Self::Diff {
        StratFilesystemDiff(if self.0 != new_state.0 {
            Some(new_state.0)
        } else {
            None
        })
    }
}
<|endoftext|>


<commit_before>//! `Source` data description.
use crate::data::RoomName;

with_base_fields_and_update_struct! {
    /// A source object, which creeps can gain energy by mining from.
    #[derive(Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct Source {
        /// The source's current energy - available to be mined be creeps.
        pub energy: u32,
        /// The source's maximum energy - what `energy` resets to on regeneration.
        pub energy_capacity: u32,
        /// The amount of energy either harvested on this source specifically or for the room since
        /// the last invasion (not sure which it is).
        #[serde(default)]
        pub invader_harvested: u32,
        /// The game time at which the source will next regenerate. `None` when waiting on a creep to
        /// first harvest it.
        pub next_regeneration_time: Option<u32>,
        /// The number of ticks between when a source is first harvested after regeneration and when it next
        /// regenerates.
        pub ticks_to_regeneration: u32,
    }

    /// The update structure for a source object.
    #[derive(Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SourceUpdate {
        - energy: u32,
        - energy_capacity: u32,
        - invader_harvested: u32,
        - next_regeneration_time: Option<u64>,
        - ticks_to_regeneration: u32,
    }
}

#[cfg(test)]
mod test {
    use serde::Deserialize;
    use serde_json;

    use crate::data::RoomName;

    use super::Source;

    #[test]
    fn parse_source_and_update() {
        let json = json!({
            "_id": "57ef9dba86f108ae6e60e2fc",
            "energy": 260,
            "energyCapacity": 3000,
            "invaderHarvested": 29240,
            "nextRegenerationTime": 19894026,
            "room": "E4S61",
            "ticksToRegeneration": 300,
            "type": "source",
            "x": 26,
            "y": 9,
        });

        let mut obj = Source::deserialize(&json).unwrap();

        assert_eq!(
            obj,
            Source {
                id: "57ef9dba86f108ae6e60e2fc".to_owned(),
                room: RoomName::new("E4S61").unwrap(),
                x: 26,
                y: 9,
                energy: 260,
                energy_capacity: 3000,
                invader_harvested: 29240,
                next_regeneration_time: Some(19894026),
                ticks_to_regeneration: 300,
            }
        );

        obj.update(
            serde_json::from_value(json!({
                "x": 40,
                "y": 50,
                "energy": 0,
            }))
            .unwrap(),
        );

        assert_eq!(
            obj,
            Source {
                id: "57ef9dba86f108ae6e60e2fc".to_owned(),
                room: RoomName::new("E4S61").unwrap(),
                x: 40,
                y: 50,
                energy: 0,
                energy_capacity: 3000,
                invader_harvested: 29240,
                next_regeneration_time: Some(19894026),
                ticks_to_regeneration: 300,
            }
        );
    }

    #[test]
    fn handle_no_invader_harvested() {
        let json = json!({
            "_id": "5bbcad499099fc012e6370ab",
            "energy": 4000,
            "energyCapacity": 4000,
            "nextRegenerationTime": null,
            "room": "E6S26",
            "ticksToRegeneration": 300,
            "type": "source",
            "x": 40,
            "y": 34,
        });

        let obj = Source::deserialize(&json).unwrap();

        assert_eq!(
            obj,
            Source {
                id: "5bbcad499099fc012e6370ab".to_owned(),
                room: RoomName::new("E6S26").unwrap(),
                x: 40,
                y: 34,
                energy: 4000,
                energy_capacity: 4000,
                invader_harvested: 0,
                next_regeneration_time: None,
                ticks_to_regeneration: 300,
            }
        );
    }
}
<commit_msg>Fix last commit.<commit_after>//! `Source` data description.
use crate::data::RoomName;

with_base_fields_and_update_struct! {
    /// A source object, which creeps can gain energy by mining from.
    #[derive(Clone, Debug, PartialEq)]
    #[serde(rename_all = "camelCase")]
    pub struct Source {
        /// The source's current energy - available to be mined be creeps.
        pub energy: u32,
        /// The source's maximum energy - what `energy` resets to on regeneration.
        pub energy_capacity: u32,
        /// The amount of energy either harvested on this source specifically or for the room since
        /// the last invasion (not sure which it is).
        #[serde(default)]
        pub invader_harvested: u32,
        /// The game time at which the source will next regenerate. `None` when waiting on a creep to
        /// first harvest it.
        pub next_regeneration_time: Option<u32>,
        /// The number of ticks between when a source is first harvested after regeneration and when it next
        /// regenerates.
        pub ticks_to_regeneration: u32,
    }

    /// The update structure for a source object.
    #[derive(Clone, Debug)]
    #[serde(rename_all = "camelCase")]
    pub struct SourceUpdate {
        - energy: u32,
        - energy_capacity: u32,
        - invader_harvested: u32,
        - next_regeneration_time: Option<u32>,
        - ticks_to_regeneration: u32,
    }
}

#[cfg(test)]
mod test {
    use serde::Deserialize;
    use serde_json;

    use crate::data::RoomName;

    use super::Source;

    #[test]
    fn parse_source_and_update() {
        let json = json!({
            "_id": "57ef9dba86f108ae6e60e2fc",
            "energy": 260,
            "energyCapacity": 3000,
            "invaderHarvested": 29240,
            "nextRegenerationTime": 19894026,
            "room": "E4S61",
            "ticksToRegeneration": 300,
            "type": "source",
            "x": 26,
            "y": 9,
        });

        let mut obj = Source::deserialize(&json).unwrap();

        assert_eq!(
            obj,
            Source {
                id: "57ef9dba86f108ae6e60e2fc".to_owned(),
                room: RoomName::new("E4S61").unwrap(),
                x: 26,
                y: 9,
                energy: 260,
                energy_capacity: 3000,
                invader_harvested: 29240,
                next_regeneration_time: Some(19894026),
                ticks_to_regeneration: 300,
            }
        );

        obj.update(
            serde_json::from_value(json!({
                "x": 40,
                "y": 50,
                "energy": 0,
            }))
            .unwrap(),
        );

        assert_eq!(
            obj,
            Source {
                id: "57ef9dba86f108ae6e60e2fc".to_owned(),
                room: RoomName::new("E4S61").unwrap(),
                x: 40,
                y: 50,
                energy: 0,
                energy_capacity: 3000,
                invader_harvested: 29240,
                next_regeneration_time: Some(19894026),
                ticks_to_regeneration: 300,
            }
        );
    }

    #[test]
    fn handle_no_invader_harvested() {
        let json = json!({
            "_id": "5bbcad499099fc012e6370ab",
            "energy": 4000,
            "energyCapacity": 4000,
            "nextRegenerationTime": null,
            "room": "E6S26",
            "ticksToRegeneration": 300,
            "type": "source",
            "x": 40,
            "y": 34,
        });

        let obj = Source::deserialize(&json).unwrap();

        assert_eq!(
            obj,
            Source {
                id: "5bbcad499099fc012e6370ab".to_owned(),
                room: RoomName::new("E6S26").unwrap(),
                x: 40,
                y: 34,
                energy: 4000,
                energy_capacity: 4000,
                invader_harvested: 0,
                next_regeneration_time: None,
                ticks_to_regeneration: 300,
            }
        );
    }
}
<|endoftext|>