Как я могу динамически вернуть выражение равенства Дизеля из вспомогательного метода? - PullRequest
1 голос
/ 04 мая 2020

У меня есть enum:

enum Role {
    Administrator,
    Sponsor,
    Bot,
}

, и в моей базе данных есть таблица под названием «роли», которая выглядит следующим образом:

| user_id | administrator | sponsor | bot   |
|---------|---------------|---------|-------|
| 0       | true          | false   | false |
| 1       | false         | false   | false |
| 2       | false         | true    | true  |

Как выполнить сопоставление для Role и получить соответствующий столбец в моей базе данных, используя Diesel , а не необработанный запрос SQL?

Я могу вернуть выражение Diesel из метода, соответствующего роли , но он не работает с моим оператором вставки:

#[macro_use]
extern crate diesel;

use diesel::{
    mysql::{Mysql, MysqlConnection},
    sql_types::{Bool, Nullable},
    BoxableExpression, Connection, ExpressionMethods, RunQueryDsl,
};
use schema::{roles, users};
use models::{NewUser};

use std::error::Error;

mod schema {
    table! {
        roles (user_id) {
            id -> Unsigned<Bigint>,
            user_id -> Unsigned<Bigint>,
            administrator -> Nullable<Bool>,
            sponsor -> Nullable<Bool>,
            bot -> Nullable<Bool>,
        }
    }

    table! {
        users (id) {
            id -> Unsigned<Bigint>,
            username -> Nullable<Varchar>,
        }
    }

    allow_tables_to_appear_in_same_query!(
        roles,
        users,
    );
}

mod models {
    use super::schema::users;

    #[derive(Insertable, PartialEq, Debug, Default)]
    #[table_name = "users"]
    pub struct NewUser<'a> {
        username: &'a str,
    }

    impl<'a> NewUser<'a> {
        pub fn new(username: &'a str) -> Self {
            Self {
                username
            }
        }
    }
}

enum Role {
    Administrator,
    Sponsor,
    Bot,
}

impl From<&Role> for Box<dyn BoxableExpression<roles::table, Mysql, SqlType = Nullable<Bool>>> {
    fn from(r: &Role) -> Self {
        match r {
            Role::Administrator => Box::new(roles::dsl::administrator),
            Role::Sponsor => Box::new(roles::dsl::sponsor),
            Role::Bot => Box::new(roles::dsl::bot),
        }
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let conn = MysqlConnection::establish("mysql://localhost/stquestion")?;

    diesel::replace_into(users::table)
        .values(&NewUser::new("test_account"))
        .execute(&conn)?;
    diesel::replace_into(roles::table)
        .values((
            roles::dsl::user_id.eq(1),
            <&Role as Into<
                Box<dyn BoxableExpression<roles::table, Mysql, SqlType = Nullable<Bool>>>,
            >>::into(&Role::Administrator)
            .eq(true),
        ))
        .execute(&conn)?;

    Ok(())
}

Приведенный выше код оставляет мне следующую ошибку компиляции:

error[E0277]: the trait bound `std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::
schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool
>>>: diesel::query_source::Column` is not satisfied
  --> src/main.rs:36:17
   |
36 |           .values((
   |  _________________^
37 | |             roles::dsl::user_id.eq(1),
38 | |             <&Role as Into<
39 | |                 Box<dyn BoxableExpression<roles::table, Mysql, SqlType = Nullable<Bool>>>,
40 | |             >>::into(&Role::Administrator)
41 | |             .eq(true),
42 | |         ))
   | |_________^ the trait `diesel::query_source::Column` is not implemented for `std::boxed::Box<dyn diesel::expr
ession::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType =
diesel::sql_types::Nullable<diesel::sql_types::Bool>>>`
   |
   = note: required because of the requirements on the impl of `diesel::insertable::Insertable<stackoverflow_quest
ions::schema::roles::table>` for `diesel::expression::operators::Eq<std::boxed::Box<dyn diesel::expression::Boxabl
eExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_ty
pes::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql
_types::Bool>, bool>>`
   = note: required because of the requirements on the impl of `diesel::insertable::Insertable<stackoverflow_quest
ions::schema::roles::table>` for `(diesel::expression::operators::Eq<stackoverflow_questions::schema::roles::colum
ns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, di
esel::expression::operators::Eq<std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions:
:schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Boo
l>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>)`
>error[E0277]: the trait bound `std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>: diesel::query_source::Column` is not satisfied
  --> src/main.rs:43:10
   |
43 |         .execute(&conn)?;
   |          ^^^^^^^ the trait `diesel::query_source::Column` is not implemented for `std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>`
   |
   = note: required because of the requirements on the impl of `diesel::insertable::InsertValues<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql>` for `diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>`
   = note: required because of the requirements on the impl of `diesel::insertable::InsertValues<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql>` for `(diesel::insertable::ColumnInsertValue<stackoverflow_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>)`
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<diesel::mysql::backend::Mysql>` for `diesel::query_builder::insert_statement::ValuesClause<(diesel::insertable::ColumnInsertValue<stackoverflow_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>), stackoverflow_questions::schema::roles::table>`
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<diesel::mysql::backend::Mysql>` for `diesel::query_builder::insert_statement::InsertStatement<stackoverflow_questions::schema::roles::table, diesel::query_builder::insert_statement::ValuesClause<(diesel::insertable::ColumnInsertValue<stackoverflow_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>), stackoverflow_questions::schema::roles::table>, diesel::query_builder::insert_statement::Replace>`
   = note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, diesel::mysql::backend::Mysql>` for `diesel::query_builder::insert_statement::InsertStatement<stackoverflow_questions::schema::roles::table, diesel::query_builder::insert_statement::ValuesClause<(diesel::insertable::ColumnInsertValue<stackoverflow_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<stackoverflow_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>), stackoverflow_questions::schema::roles::table>, diesel::query_builder::insert_statement::Replace>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
error: could not compile `stackoverflow-questions`.

To learn more, run the command again with --verbose.

Я создал воспроизводимую пример для этой проблемы здесь .

1 Ответ

0 голосов
/ 05 мая 2020

Использование BoxableExpression, на мой взгляд, неправильный путь. Гораздо проще реализовать Insertable<roles::table> вручную для вашего перечисления, а затем напрямую вставить значения оттуда.

Используя эту Insertable impl

impl Insertable<roles::table> for Role {
    type Values = <(
        diesel::dsl::Eq<roles::administrator, bool>,
        diesel::dsl::Eq<roles::sponsor, bool>,
        diesel::dsl::Eq<roles::bot, bool>,
    ) as Insertable<roles::table>>::Values;

    fn values(self) -> Self::Values {
        use crate::schema::roles;

        match self {
            Role::Administrator => (
                roles::administrator.eq(true),
                roles::sponsor.eq(false),
                roles::bot.eq(false),
            ),
            Role::Sponsor => (
                roles::administrator.eq(false),
                roles::sponsor.eq(true),
                roles::bot.eq(true),
            ),
            Role::Bot => (
                roles::administrator.eq(false),
                roles::sponsor.eq(false),
                roles::bot.eq(true),
            ),
        }
        .values()
    }
}

, можно написать свою вставку запросить следующий способ:

    diesel::replace_into(roles::table)
        .values((roles::dsl::user_id.eq(1), Role::Administrator))
        .execute(&conn)?;
...