🦀

Rust Diesel で enum を扱う

2023/09/26に公開

やりたいこと

Rust Diesel で Enum を使用してデータを保存します。

Usecase

スケジュール管理のアプリで Event model に繰り返しか単発かを判定する RepeatType Enum の実装。

実装

Mysql

repeat_type が永続化させるためのフィールドです。
今回は TINYINT 型でフィールドを作成します。

CREATE TABLE IF NOT EXISTS events (
  id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
  title VARCHAR(255) NOT NULL,
  start_at TIMESTAMP NOT NULL,
  end_at TIMESTAMP NOT NULL,
  repeat_type TINYINT NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
  deleted_at TIMESTAMP
);

RepeatType enum model (Rust)

RepeatType という enum の実装します。
RepeatType に以下の関数を implement します。

  • from: Applicationから渡される値からRepeatTypeを返します。
  • from_sql: SQLのデータ取得時にデータからRepeatTypeを返します。
  • to_sql: RepeatTypeを保存するときにSQLに対応するために値を変換します。
#[derive(Debug, AsExpression, FromSqlRow, Deserialize, Serialize)]
#[diesel(sql_type = diesel::sql_types::TinyInt)]
pub enum RepeatType {
    Once = 1,
    Recurrence = 2,
}

impl From<i8> for RepeatType {
    fn from(i: i8) -> Self {
        match i {
            1 => RepeatType::Once,
            2 => RepeatType::Recurrence,
            _ => panic!("Invalid value for RepeatType"),
        }
    }
}

impl<DB: Backend> FromSql<TinyInt, DB> for RepeatType
where
    DB: Backend,
    i8: FromSql<TinyInt, DB>,
{
    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
        match i8::from_sql(bytes)? {
            1 => Ok(RepeatType::Once),
            2 => Ok(RepeatType::Recurrence),
            x => Err(format!("Unrecognized variant {}", x).into()),
        }
    }
}

impl<DB> ToSql<TinyInt, DB> for RepeatType
where
    DB: Backend,
    i8: ToSql<TinyInt, DB>,
{
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
        match self {
            RepeatType::Once => 1.to_sql(out),
            RepeatType::Recurrence => 2.to_sql(out),
        }
    }
}

Event model (Rust)

Event model に Repeat Type を実装します。

#[derive(Identifiable, Queryable, Debug, Serialize)]
#[diesel(table_name = events)]
#[diesel(check_for_backend(diesel::mysql::Mysql))]
pub struct Event {
    pub id: String,
    pub title: String,
    pub start_at: NaiveDateTime,
    pub end_at: NaiveDateTime,
    pub repeat_type: RepeatType,
    pub created_at: NaiveDateTime,
    pub updated_at: NaiveDateTime,
    pub deleted_at: Option<NaiveDateTime>,
}

代案

diesel-derive-enum という crate もあるが自分の環境だと思うように動作しなかったので今回は使用してませんが、上記の実装を簡単にしてくれる crate なのでおすすめです。
https://crates.io/crates/diesel-derive-enum

Discussion