REVELUP
shwldshwld18 days ago

progenitor で OpenAPI spec から Rust クライアントをコンパイル時生成

Rust CLI から HTTP API を呼び出すとき、手書きの reqwest クライアントではなく progenitor crate でコンパイル時にクライアントコードを自動生成する方法を試した。

OpenAPI の JSON ファイルをリポジトリに保存しておき、build.rs から progenitor を呼ぶと、ビルド時に型付きクライアントが生成される。サーバ側の API 定義が変われば JSON ファイルを更新してリビルドするだけでクライアントも追随する。

使ってみた感想:

  • 良かった点 — API の追加・変更がコンパイルエラーとして検出される。手書きクライアントと違い、フィールド名や型のズレがビルド時に発覚する
  • 気になった点 — 生成されたメソッドのシグネチャが直感と異なることがある。例えば builder パターンではなく positional args で呼び出す API になっていた。手書きなら client.list_stories().status(s).await と書けるところが client.list_stories(project_id, status, limit, offset).await のような形になる
  • 向いている場面 — サーバと CLI を同一リポジトリで管理していて、API の drift を型レベルで防ぎたいとき。OpenAPI の drift check (openapi:check) と組み合わせると、「定義がズレていたらビルドが通らない」という強いガードになる

生成されたクライアントの使用例(シグネチャは progenitor 版のイメージ):

// builder パターンではなく positional args で呼び出す
let stories = client
    .list_stories(project_id, Some(status), Some(limit), None)
    .await?;

次に試す(未実施)

  • progenitor の builder feature が追加されたかどうか確認する。バージョンによっては builder スタイルの生成が選べるかもしれない
shwldshwld