---
title: Rust
description: Rust 用の SDK。
---

[`subako-function-sdk`](https://crates.io/crates/subako-function-sdk) は、普通の Rust の関数を Tool にします。関数名が Tool の名前に、doc コメントがモデルの読む説明になります。入力と出力の型には `JsonSchema` を derive し、それが Tool の Schema になります。

## Package を始める

[Rust テンプレート](https://github.com/subako-ai/subako-function-template-rust)を使うと、crate、ツールチェーン、Bundle をビルドするスクリプトがそろいます。

```bash
rustup target add wasm32-wasip1
cargo install cargo-generate
cargo generate subako-ai/subako-function-template-rust --name orders
```

自分の crate に SDK を追加することもできます。

```toml Cargo.toml
[dependencies]
schemars = "1"
serde = { version = "1", features = ["derive"] }
subako-function-sdk = "0.2"

[profile.release]
opt-level = "s"
lto = true
```

## Tool

```rust src/main.rs
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use subako_function_sdk::{ToolError, function, package};

#[derive(Deserialize, JsonSchema)]
struct LookupInput {
    /// The order's number, such as 1042.
    order: u32,
}

#[derive(Serialize, JsonSchema)]
struct Order {
    status: String,
    total_cents: u64,
}

/// Look an order up by its number.
#[function]
fn lookup(input: LookupInput) -> Result<Order, ToolError> {
    if input.order == 0 {
        return Err(ToolError::new("order numbers start at 1"));
    }
    Ok(Order { status: "shipped".into(), total_cents: 12_900 })
}

/// Cancel an order that has not shipped.
#[function]
fn cancel(input: LookupInput) -> Result<Order, ToolError> {
    Err(ToolError::new(format!("order {} has already shipped", input.order)))
}

package! {
    name: "orders",
    description: "Looks orders up, and cancels them.",
    functions: [lookup, cancel],
}
```

- `#[function]` を付けた関数には、必ず doc コメントを書きます。これがモデルの読む説明になり、doc コメントがないとビルドが失敗します。
- 関数は `Deserialize + JsonSchema` を満たす引数を 1 つ取り、`Result<O, E>` を返します。`O` は `Serialize + JsonSchema` を満たし、`E` は `ToolError` に変換できる型です。
- フィールドの doc コメントは、Schema のプロパティの説明になります。モデルは何を渡せばよいかをここから知るので、モデルに向けて書いてください。
- `package!` では Package の名前と含める関数を指定します。`main` はこのマクロが生成します。

## エラー

`ToolError::new` は、モデルが読むエラーを作ります。`String`、`&str`、`std::io::Error`、SDK の `HttpError` から変換できるので、`?` を使えば失敗の理由がそのままモデルに渡ります。Schema に合わない入力には、関数を実行する前にエラーが返されます。

panic させずにエラーを返してください。panic はクラッシュになり、モデルには Function が止まったことしか伝わりません。

## ファイル

[Volume と Asset](/ja/functions/runtime#ファイルシステム) は `std::fs` で読み書きします。

```rust
let csv = std::fs::read_to_string("/volume/tmp/export.csv")?;
std::fs::create_dir_all("/volume/data/reports")?;
std::fs::write("/volume/data/reports/q3.md", summary)?;
let template = std::fs::read_to_string("/package/templates/report.md")?;
```

## HTTP

`http::request` は、Subako を通してリクエストを送ります。Grant のネットワークと Credential の設定が適用されます。

```rust
use subako_function_sdk::http::{self, Request, RequestBody, ResponseBody, ResponseOutput};

/// Fetch a page's text.
#[function]
fn fetch(input: FetchInput) -> Result<Page, ToolError> {
    let response = http::request(&Request::get(input.url))?;
    match response.body {
        ResponseBody::Inline { text } => Ok(Page { status: response.status, text }),
        ResponseBody::File { .. } => Err(ToolError::new("the body was saved to a file")),
    }
}
```

レスポンスの本文は、`http::INLINE_MAX_BYTES` までならテキストとしてインラインで返ります。それより大きい本文やバイナリの本文は、リクエストの `output` を `ResponseOutput::File { path }` にすれば Volume に直接書き出せます。逆に Volume のファイルを本文として送るには、`RequestBody::File { path }` を使います。

## 画像

`Answer` を返すと、値に加えて Volume の画像をモデルに見せられます。

```rust
use subako_function_sdk::{Answer, ToolError, function};

/// Plot a chart and show it.
#[function]
fn plot(input: PlotInput) -> Result<Answer<Plot>, ToolError> {
    let path = "/volume/tmp/chart.png".to_string();
    // ... `path` にグラフを書き出します ...
    Ok(Answer::new(Plot { path: path.clone() }).with_image_file(path))
}
```

## ビルドとテスト

```bash
cargo test                                         # Tool は普通の Rust として実行されます
cargo build --release --target wasm32-wasip1       # target/wasm32-wasip1/release/orders.wasm
cargo run -- --subako-manifest > subako-package.json
```

手元のマシン向けにビルドした crate は、`--subako-manifest` を付けて実行すると `subako-package.json` を出力します。Manifest をコードから生成するので、両者が食い違うことはありません。テンプレートの `package.sh` はこの 3 つをまとめて実行し、そのまま[アップロード](/ja/functions/quickstart#アップロードする)できる `dist/<name>.tar.gz` を作ります。

API リファレンスは [docs.rs](https://docs.rs/subako-function-sdk) にあります。
