cornucopiaとは (2023-05-26) 
SQLからRustのコードを生成して安全にデータベース操作ができる。恐らくGoの
sqlc と同じ感じなんだと思う。
というかcornucopiaって何よ 
1
 
resources 
準備 (2023-05-26) 
(2023-05-26) 
Rustのプロジェクトを作成
cargo new --bin cornucopia-example 
(2023-05-26) 
PostgreSQLのサーバーを適当に建てる
version :  "3" 
services : 
  db : 
    image :  postgres:13.3 
    environment : 
      POSTGRES_USER :  test 
      POSTGRES_PASSWORD :  password 
    ports : 
      -  5432:5432 
    volumes : 
      -  postgres:/var/lib/postgresql 
volumes : 
  postgres : 
(2023-05-26) 
cornucopiaにはmigration機能などはついていない。
まず最初にデータベースのスキーマを作る必要がある。
ここでは
atlas を使って適当にスキーマを決める
schema  "public"  {} 
table  "users"  { 
  schema  = schema. public 
  column  "id"  { 
    null  =  false 
    type  = int 
    identity  { 
        generated  = ALWAYS 
        start  =  10 
        increment  =  10 
    } 
  } 
  primary_key   { 
    columns  = [ column . id ] 
  } 
  column  "name"  { 
    null  =  false 
    type  = text 
  } 
} 
そして適用
atlas schema apply --url "postgresql://test:password@localhost:5432/test?sslmode=disable" --to "file://schema.hcl" 
使い方 (2023-05-26) 
ここから実際にcornucopiaを使う
クエリの作成 (2023-05-26) 
Rustプロジェクトのルートにqueriesフォルダを作り、そこにSQLを置く
--! insert_user 
INSERT INTO  users( name ) 
VALUES  (: name ); 
インストール (2023-05-26) 
cliをインストール
生成 
cornucopiaコマンドでRustのファイルを生成できる。ちなみにcornucopiaがpostgresのdockerコンテナを勝手に作るようにもできるみたい。
cornucopia live "postgresql://test:password@localhost:5432/test" 
rustfmt --edition 2021 ./src/cornucopia.rs 
このコマンドでは実際のデータベースに接続することで存在しないテーブルにアクセスしようとした際などにエラーを出してくれる。とても便利。
src/cornucopia.rsが生成される。先のSQLではこのようなファイルが得られる。
// This file was generated with `cornucopia`. Do not modify. 
#[allow(clippy::all, clippy::pedantic)] 
#[allow(unused_variables)] 
#[allow(unused_imports)] 
#[allow(dead_code)] 
pub mod  types {} 
#[allow(clippy::all, clippy::pedantic)] 
#[allow(unused_variables)] 
#[allow(unused_imports)] 
#[allow(dead_code)] 
pub mod  queries { 
    pub mod  user { 
        use  cornucopia_async :: GenericClient ; 
        use  futures; 
        use  futures ::{ StreamExt ,  TryStreamExt }; 
        pub fn  insert_user () ->  InsertUserStmt  { 
            InsertUserStmt ( cornucopia_async :: private :: Stmt :: new ( 
                "INSERT INTO users(name) 
VALUES ($1)" , 
            )) 
        } 
        pub struct  InsertUserStmt ( cornucopia_async :: private :: Stmt ); 
        impl  InsertUserStmt  { 
            pub async fn  bind <' a ,  C :  GenericClient ,  T1 :  cornucopia_async :: StringSql >( 
                &' a  mut self , 
                client : &' a C , 
                name : &' a T1 , 
            ) ->  Result < u64 ,  tokio_postgres :: Error > { 
                let  stmt  =  self . 0. prepare ( client ). await ?; 
                client . execute ( stmt , &[ name ]). await 
            } 
        } 
    } 
} 
とてもわかりやすい
使用方法 (2023-05-26) 
この生成されたファイルの依存をいろいろ追加する。
cargo add tokio cornucopia_async futures tokio_postgres 
なおデフォルトではtokio_postgresを用いた非同期コードが生成されるが同期コードも生成できるみたい。
あとは生成された関数を呼び出すだけ。
use  cornucopia :: queries :: user ::insert_user; 
use  tokio_postgres ::{ Error ,  NoTls }; 
mod  cornucopia; 
#[tokio::main] 
async fn  main () ->  Result <(),  Error > { 
    let  ( client ,  connection ) =  tokio_postgres :: connect ( 
        "host=localhost port=5432 user=test password=password" , 
        NoTls , 
    ) 
    . await ?; 
    tokio :: spawn ( async move  { 
        if  let  Err ( e ) =  connection . await  { 
            eprintln! ( "connection error: {}" ,  e ); 
        } 
    }); 
    insert_user (). bind (& client , & "me" ). await . unwrap (); 
    Ok (()) 
} 
良かったところ (2023-05-26) 
今までRustでRDBを扱う方法を色々さがしてきて
ORM: まだあまり成熟してない感じだった(個人的にはprisma-client-rustが一番よかったと感じたが依存が重すぎなのと色々不安定だった) 
sqlx: 確かにSQLを書けばマクロで型が補完されるのはすごいが開発中もデータベースの状態を気にしないといけないし何よりマクロは辛い 
 
などの問題を感じていたがcornucopiaはデータベースに接続するのはコードを生成するときだけだし生成されたコードも普通のRustファイルで見やすいのがとても良い。
あとbind()以外にもparams()関数が用意されていてパラメータを構造体で作成できるのも嬉しい。
改善されてほしいところ (2023-05-26) 
まだ色々機能が足りてない感じがする
 
PostgreSQLにしか対応してない
まあこれは仕方ないのかなと思いつつもsqliteとかで使えたらとてもいいなと