Open9

[Rust] tokioのORM「toasty」を今のうちに少しだけ

KenKen

PrismaライクなORMらしい。まだ開発初期段階で本番には使えないとのこと。
asyncに対応(DBアクセスは非同期の方が優れているという意味ではなく、あくまでこのORMの特徴として)

KenKen

触ってみる

現在時(2024/12/20)、まだcrates.ioには公開されていないようなので直接リポジトリの中身を覗いてみる。とりあえずcloneする。

$ git clone https://github.com/tokio-rs/toasty.git

現在時点での最新のコミットはce4e4a6fe25cb3b2c400331ed125d4a82ed960ff

KenKen

examplesディレクトリ内にサンプルがいくつか入っている。各サンプルに存在するschema.toastyファイルがPrismaでいうところのschema.prismaファイルに相当すると思われる。

toasty/examples
├── composite-key
│   ├── Cargo.toml
│   ├── schema.toasty  ← これ
│   └── src
│       ├── db
│       │   ├── ...
│       └── main.rs
├── cratehub
│   ├── Cargo.toml
│   ├── schema.toasty  ← これ
│   └── ...
├── hello-toasty
│   ├── ...
└── ...
KenKen

hello-toastyTodoモデルに少し変更を加えてみる。examples/hello-toasty/schema.toastyTodoにStringのcontentとOption<String>のremarksを追加する。

examples/hello-toasty/schema.toasty
model Todo {
    ...

    content: String,

    remarks: Option<String>,
}
KenKen

コード生成コマンドを実行

$ cargo run -p toasty-cli -- gen --schema "examples/hello-toasty/schema.toasty" "examples/hello-toasty/src/db"

examples/hello-toasty/src/db ディレクトリ内の関連ファイルが更新され、contentフィールドとremarksフィールドの情報や関連メソッドが追加されていることが確認できる。

examples/hello-toasty/src/db/todo.rs
 pub struct Todo {
     ...
     pub content: String,
     pub remarks: Option<String>,
 }
 
 ...
 
 pub fn content(mut self, content: impl Into<String>) -> Self {
     self.stmt.set_value(4, content.into());
     self
 }
 pub fn remarks(mut self, remarks: impl Into<String>) -> Self {
     self.stmt.set_value(5, remarks.into());
     self
 }
KenKen

examples/hello-toasty/src/main.rsTodoを挿入する箇所にて、content, remarksの値をセットしてみる。

examples/hello-toasty/src/main.rs
 // 77行目〜
 let todo = u2
     .todos()
     .create()
     .title("finish toasty")
     .content("as always")  // 追加
     .remarks("by today")  // 追加
     .exec(&db)
     .await
     .unwrap();
 
 ...
 
 // 113行目〜
 let mut user = User::create()
     .name("Ann Chovey")
     .email("ann.chovey@example.com")
     .todo(
         Todo::create()
             .title("Make pizza")
             .content("in the mood for Margherita pizza"),  // 追加
     )
     .todo(
         Todo::create()
             .title("Sleep")
             .content("Up early tomorrow.")  // 追加
             .remarks("By 11 p.m. on December 20, 2024."),  // 追加
     )
     .exec(&db)
     .await
     .unwrap();
 
 user.update()
     .todo(
         Todo::create()
             .title("might delete later")
             .content("without forgetting"),  // 追加
     )
     .exec(&db)
     .await
     .unwrap();
 
 // 登録内容確認用に上の処理に続けて以下の一連を追加
 let mut todos = user.todos().all(&db).await.unwrap();
 
 while let Some(todo) = todos.next().await {
     let todo = todo.unwrap();
     println!("TODO = {todo:#?}");
 }
KenKen

hello-toastyを実行してみる。

$ cargo run -p example-hello-toasty

↓ 結果

 ...
 TODO = Todo {
     ...
     content: "as always",
     remarks: Some(
         "by today",
     ),
 }
 ...
 TODO = Todo {
     ...
     title: "Make pizza",
     content: "in the mood for Margherita pizza",
     remarks: None,
 }
 TODO = Todo {
     ...
     content: "Up early tomorrow.",
     remarks: Some(
         "By 11 p.m. on December 20, 2024.",
     ),
 }
 TODO = Todo {
     ...
     title: "might delete later",
     content: "without forgetting",
     remarks: None,
 }

追加したcontent, remarksの値が登録および取得できていることが確認できた。

KenKen

所感

Prismaっぽい。

各フィールドの値をsetter的メソッドによって個別にセットするやり方は、項目のセットし忘れをコンパイル段階で弾けない等々ありそう...
(項目追加対応時、コンパイルエラーが起きてる箇所だけなおせば対応完了!みたいなのもやりづらい)

まだこれから洗練されていくと思われるので注目しておきたい。