Docsの目次

ライブラリとRust連携の設計案

このページは、現在の再利用方法と追加機能の設計案をまとめたものです。moduleの名前空間、不透明なresource型、汎用DB APIはまだ実装されていません。未実装の構文案は、現在の実行例とは区別してください。

目次 · 現在使える機能: importとRust連携、nagi.toml

Nagiで書いた型・検証・計算を複数のアプリで共有し、通信や保存には既存のRustライブラリを組み合わせる構成を目指します。CLI、HTTP、バッチ処理ごとに業務規則を書き直す必要を減らします。

このページの目次

現在の対応と不足している部分

部分 現在 提案する追加
import 相対ファイルを同じ名前空間に読み込む module、from、as、別moduleの同名定義
Rust連携 sync/asyncのexternと型付きの引数・戻り値 接続やclientを表す不透明な型
Rustファイル rust.file で1つのnative moduleを指定。共有crateは依存tableで指定 —
Cargo依存 version文字列、またはversion・path・features・default-features・packageのtable —
ランタイム HTTP、JSON、SQLite等を常に依存に含む 必要な機能に合わせた依存とコード生成
DB SQLiteと固定したbind引数 型付きの任意個の引数、行読み取り、transaction

現在のexternはRustのAPIを自動でimportする機能ではありません。Rust固有の型は、既知の数値・str・class・List・Result等へ変換します。Nagiの check は宣言と呼び出しを検査し、Rust実装との一致は build で確認します。externからviewを返すことは未対応です。

共有する処理とアダプター

共有するNagiファイルにデータ型・検証・計算を置き、入口で入出力を組み合わせます。Rustのアダプターは外部ライブラリの型をNagiのデータ型へ変換します。独立したRust crateは自身の型を使い、生成アプリのclassへの変換はアダプターに置きます。

現在の6つの例は、この分け方を既存APIで試すものです。

プロジェクト 再利用と連携の例
foundation-cli 共有の foundation.nagi とRustの pricing.rs をCLIから使う
foundation-report 同じ検証・計算をJSONレポートに使う
rust-json serde_jsonの結果をNagiのclassへ変換する
rust-async Nagiの実行環境でRustのTokioタイマーをawaitする
custom-http RustのAxum/TokioサーバーへNagiの同期callbackを渡す
low-kernel アプリの処理から手書きLowの計算を呼ぶ

rust.file が1つでも、Rustの mod や #[path] で実装を分割できます。custom-httpは組み込みのserveやDbを使いません。HTTPの制限・停止処理はそのRust側で管理し、組み込みHTTPの設定が自動適用されるとは扱いません。

moduleと名前解決

次は未実装の構文案です。

import json
import sqlite as storage
from json import decode as decode_json
import "domain/orders.nagi" as orders
from "domain/orders.nagi" import Order as SavedOrder

orders.Order と SavedOrder は同じ定義を指し、別ファイルの同名classは別の型です。moduleと定義にIDを持たせ、型検査、High→Low、Rust出力、エディターが同じ解決結果を使います。別名を文字列置換して実装しません。

標準moduleは同梱の定義、引用符付きimportは相対ファイルを指します。importだけでDB接続や通信は始めません。初版はそのファイルに定義した関数・classを公開し、importした名前を自動で再公開しません。既存の平らなimportと組み込み関数は互換入口として保ちます。

Cargoの依存設定

version文字列と次のtable形式に対応しています。設定の詳細と実行例はnagi.tomlを参照してください。

[rust]
file = "adapters/native.rs"

[rust.dependencies]
serde_json = "1.0"
foundation = { package = "my-foundation", path = "../my-foundation" }
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }

pathはnagi.toml基準で解決し、生成先を変えても同じcrateを参照します。check・lower・symbolsはCargoを呼ばず、依存crateの存在を要求しません。実際のpath・version・Rust APIはbuild/runでCargoが検査します。

CLIの--rust-dep NAME=VERSIONは、同名のtable全体をversion文字列に置換します。元のpath・package・features・default-featuresは残りません。

Cargoのfeaturesは依存経路ごとに合成されます。直接依存でdefault-featuresを無効にしても、別の経路が有効にした機能までは外れません。生成先の既存Cargo.lockは再ビルド時も保持します。lockは解決した版を記録しますが、path依存のソース内容は固定しません。nagic build --lockedは未対応です。固定した解決でのビルドは、生成したCargo.tomlに対してCargoの--lockedを指定します。

ランタイムを機能ごとに選ぶ

core・async・json・http・sqlite・postgresに分ける案です。出力する関数・型・組み込みの解決結果から必要な機能を集めます。全関数を出力する段階では、入口から直接呼ぶものだけを見て依存を外しません。

classへのSerde/行読み取りの生成、公開型、エラー変換も切り替える必要があります。Cargoのoptional化だけでは終わりません。Rust連携には互換設定を保ち、新しいアダプターは必要なランタイム機能を明示できる形にします。依存crateのfeatureは生成アプリの同名featureへ自動では伝わりません。

組み込みHTTPには serve(port) を追加し、既存の serve(db, port) を保つ案です。登録routeにDb引数があれば引数1個のserveをエラーにします。これはcustom-httpのような現在の独自サーバーとは別の変更です。

不透明な型と非同期処理

clientや接続プールをNagiへ公開するには、型名とRust型の対応、操作、所有権の登録が必要です。現在のclassはデータ用であり、このresource型の代わりにはしません。

契約 必要な扱い
所有・共有 原則move。Copyにせず、共有・cloneを許す型だけ明示する
借用 読み取りと排他的な操作を分け、await中もownerを保つ
型と変換 別providerの同名型を区別し、JSON/DB用deriveを自動追加しない
スレッド Sendはスレッド間の移動、Syncは参照の共有を許す。両方を無条件に要求・付与しない
終了 close・commit・rollbackとdrop時の残り処理を定義する

通常のDropではawaitできません。必要な非同期終了処理は明示的に用意します。futureのdropで呼び出し側をキャンセルしても、別workerや既に送ったDB書き込みが取り消される保証はありません。終了・再試行・二重書き込みへの対応は操作ごとに決めます。

汎用DB APIの前提

現在のdb_insertはstrとi32、db_updateはi64・str・i32を取ります。互換用に残し、任意のテーブルや条件に対応したAPIとしては説明しません。

部分 先に定義する契約
引数 任意個の型付き値、NULLの型、str/bytes/viewの所有権
操作 execute/one/all。対象なしはOption、bindなしも同じ規則で扱う
行 元のfield名で列を対応付け、列順・NULL・整数範囲・型違いを扱う
transaction 1接続を保持し、commit/rollbackで消費する。終了後の使用と同時操作を禁止する
エラー 接続・待機・SQL・decode・対象なしを区別する

型付きの可変長引数を第1候補とします。SQLとschemaの一致はbackendでも検査し、動的SQLまでコンパイル時に保証しません。transactionの排他的借用には現在のread-only viewとは異なる扱いが必要です。

SQLiteの ?1、PostgreSQLの $1、i64に対応するBIGINTやidentity等の違いは保存層で扱います。SQLは自動翻訳せず、poolの別々の呼び出しでBEGIN/COMMITを送るtransactionも作りません。一般利用向けPostgreSQL対応は、引数・行・transactionの契約と実DB試験が揃ってから案内します。

実装する順序

  1. 現在の例で共有処理とadapterの境界を確認し、Nagiの検査とRust buildを両方通す。
  2. 実装済みの依存tableとlockの維持を土台に、ランタイム機能の選択とderive生成を整える。
  3. module、from/as、High→Low、エディターを同じ名前解決へ揃える。
  4. 組み込みHTTP起動をDBから分け、DBなしではworkerもSQLite依存も不要にする。
  5. resourceの所有・借用・キャンセルと汎用DB契約をSQLiteで検証する。
  6. PostgreSQLで同じ契約、TLS・pool・timeout・停止を実DB検証し、保存先を変えても同じ業務処理を使う例を作る。

旧import・組み込み・Rust連携を保ち、採用した部分から移行できるようにします。各段階で既存例、High/Low、エディター、対象OSを確認します。依存削減はCargoのfeatures、ビルド時間、出力容量で測ります。

参考: Cargo依存指定、featuresと合成、Cargo.lock、Rust module、Send/Sync、Future、Drop、Tokioのキャンセル。

Nagi 0.1のドキュメントこのページのソース