Search code examples
postgresqlrustenumsrust-diesel

How to use a C-like enum with diesel


I have a users table that I'm trying to add a role field to with Diesel:

#[derive(Debug, Clone, Queryable, Insertable)]
pub struct User {
    // other fields
    pub role: Role,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression)]
#[sql_type = "diesel::sql_types::VarChar"]
pub enum Role {
    User,
    Admin,
}

/// manual impls for FromSql<VarChar, Pg>, ToSql<VarChar, Pg> and FromSqlRow<VarChar, Pg>

For compatibility reasons, I'd like these to be represented in the database as varchars ("user" and "admin"), but would like to use an enum on the Rust side for extra type safety.

However, with the setup I currently have, I get:

error[E0277]: the trait bound `Role: Queryable<Text, Pg>` is not satisfied
  --> src/state/database/users.rs:47:32
   |
47 |         self.exec(|conn| users.load(conn)).await
   |                                ^^^^ the trait `Queryable<Text, Pg>` is not implemented for `Role`
   |
   = note: required because of the requirements on the impl of `Queryable<(diesel::sql_types::Uuid, Text, Text, diesel::sql_types::Timestamp, Text), Pg>` for `(model::types::UserId, model::types::Email, model::types::PasswordHash, NaiveDateTime, Role)`
   = note: 1 redundant requirement hidden
   = note: required because of the requirements on the impl of `Queryable<(diesel::sql_types::Uuid, Text, Text, diesel::sql_types::Timestamp, Text), Pg>` for `user::User`
   = note: required because of the requirements on the impl of `LoadQuery<PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>, user::User>` for `schema::users::table`

From what I understand, in diesel, VarChar and Text are the same type (i.e. aliased). If I change all of the VarChars to Texts in my impls, the error persists.

I'm confident it's not the other fields/field order because if I replace Role with a plain String, it works.

I feel like I must be missing something, this seems like fairly common behaviour. Perhaps there's another trait I need to implement?

FWIW, my schema for the users table looks like:

table! {
    users (id) {
        id -> Uuid,
        email -> Varchar,
        password_hash -> Varchar,
        created_at -> Timestamp,
        role -> Varchar,
    }
}

Using postgres 14, rust 1.57, diesel 1.4.8


Solution

  • You need to derive FromSqlRow as well. It will generate the necessary FromSqlRow and Queryable impl for your type.