RustのORMであるDieselを使ってみる

## 概要

RustのORMではDieselが有名のようなので使ってみた手順のメモです。
基本的に下記の Getting Started の流れを実施しています。
https://diesel.rs/guides/getting-started


## プロジェクトの生成

```console
% cargo new diesel_hello_world
% cd diesel_hello_world   
```


## Cargo.tomlにdieselとdotenvを追加

```toml
[dependencies]
diesel = { version = "1.4.4", features = ["postgres"] }
dotenv = "0.15.0"
```


## dockerでpostgresqlを立て、Databaseを作る

```yml
version: "3.9"

services:
  postgres:
    image: postgres:14.3-alpine3.16
    environment:
      POSTGRES_USER: username
      POSTGRES_PASSWORD: password
    ports:
      - 5432:5432
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
```
```console
% docker-compose up -d postgres 
% psql -h 127.0.0.1 -p 5432 -U username
# create database diesel_demo;
```

接続用のenv設定をしておく。
```console
echo DATABASE_URL=postgres://username:password@localhost/diesel_demo > .env    
```

## diesel_cli のインストール

dieselはcliを持っています。
```console
% cargo install diesel_cli   
```

こちらのコメントでdieselの初期化をします。
```
% diesel setup 
```

## 検証用のテーブルを作成する

migrationファイルを生成し、検証用のテーブルの定義を記載します。
```console
% diesel migration generate create_posts  
```

```sql
-- migrations/2022-06-01-044213_create_posts/up.sql

CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  title VARCHAR NOT NULL,
  body TEXT NOT NULL,
  published BOOLEAN NOT NULL DEFAULT 'f'
)

-- migrations/2022-06-01-044213_create_posts/down.sql
DROP TABLE posts
```

migrationを実行します。
```
% diesel migration run
Running migration 2022-06-01-044213_create_posts
```

psqlで確認します。

```sql
# \d posts
                                   Table "public.posts"
  Column   |       Type        | Collation | Nullable |              Default              
-----------+-------------------+-----------+----------+-----------------------------------
 id        | integer           |           | not null | nextval('posts_id_seq'::regclass)
 title     | character varying |           | not null | 
 body      | text              |           | not null | 
 published | boolean           |           | not null | false
Indexes:
    "posts_pkey" PRIMARY KEY, btree (id)


# select * from __diesel_schema_migrations ;
    version     |           run_on           
----------------+----------------------------
 00000000000000 | 2022-06-01 04:40:45.143513
 20220601044213 | 2022-06-01 04:44:23.940689
(2 rows)
```

検証用データをinsertしておきます。

```sql
# insert into posts values (1, 'title1', 'body1', true), (2, 'title2', 'body2', true);
```


## Rustでselectをするプログラムを作成する

```rs
# src/lib.rs
#[macro_use]
extern crate diesel;
extern crate dotenv;

pub mod schema;
pub mod models;

use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;

pub fn establish_connection() -> PgConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url)
        .expect(&format!("Error connecting to {}", database_url))
}
```

```rs
# src/models.rs

#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}
```

```rs
# src/bin/show_posts.rs

extern crate diesel_hello_world;
extern crate diesel;

use self::diesel_hello_world::*;
use self::models::*;
use self::diesel::prelude::*;

fn main() {
    use diesel_hello_world::schema::posts::dsl::*;

    let connection = establish_connection();
    let results = posts.filter(published.eq(true))
        .limit(5)
        .load::<Post>(&connection)
        .expect("Error loading posts");

    println!("Displaying {} posts", results.len());
    for post in results {
        println!("{}", post.title);
        println!("----------\n");
        println!("{}", post.body);
    }
}
```


## 実行

```console
% cargo run --bin show_posts
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/show_posts`
Displaying 2 posts
title1
----------

body1
title2
----------

body2
```