> ## Documentation Index
> Fetch the complete documentation index at: https://docs.verglas.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK

> Use the native streaming client and worker contract from Rust.

The Rust SDK provides an authenticated client, Arrow IPC streaming, exact table-definition checks, resumable commit following, and the native worker contract.

## Install from the repository

Add the current workspace crate as a path dependency:

```toml Cargo.toml theme={null}
[dependencies]
verglas-sdk = { path = "../verglas/sdks/rust" }
arrow-array = "57"
arrow-schema = "57"
futures = "0.3"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

## Connect

`ConnectOptions::from_env` reads the standard endpoint and token variables. It defaults the endpoint to `http://127.0.0.1:8334`.

```rust theme={null}
use verglas_sdk::{Client, ConnectOptions};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::connect(ConnectOptions::from_env()).await?;
    println!("catalog: {}", client.catalog_uri());
    println!("cache: {:?}", client.s3_endpoint());
    Ok(())
}
```

Supply every destination explicitly inside an injected container environment:

```rust theme={null}
let client = Client::connect(
    ConnectOptions::new("https://tenant.query.verglas.dev")
        .with_token(std::env::var("VERGLAS_TOKEN")?)
        .with_catalog_uri("https://catalog.example.com")
        .with_warehouse("acme-production")
        .with_s3_endpoint("https://tenant.s3.verglas.dev"),
)
.await?;
```

## Create or verify a table

`ensure_table` creates a missing table and rejects an existing table whose definition differs from the requested contract.

```rust theme={null}
use verglas_sdk::{ColumnSpec, PartitionSpec, TableDefinition};

let definition = TableDefinition {
    schema: vec![
        ColumnSpec::required("id", "string"),
        ColumnSpec::required("created_date", "date32"),
        ColumnSpec::required("total", "double"),
    ],
    partitions: vec![PartitionSpec::month("created_date")],
};

let state = client
    .ensure_table("analytics.orders", &definition)
    .await?;
println!("table state: {state:?}");
```

## Append Arrow batches

The client commits each incoming `RecordBatch` independently. It derives a unique commit key for each batch from the supplied run key.

```rust theme={null}
use std::sync::Arc;
use arrow_array::{Int64Array, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use futures::stream;
use verglas_sdk::ClientError;

let schema = Arc::new(Schema::new(vec![Field::new("id", DataType::Int64, false)]));
let batch = RecordBatch::try_new(
    schema,
    vec![Arc::new(Int64Array::from(vec![1042, 1043]))],
)?;

let result = client
    .append_stream(
        "analytics.orders",
        stream::iter(vec![Ok::<_, ClientError>(batch)]),
        "orders-import-2026-08-04",
    )
    .await?;

println!("{} rows in {} commits", result.rows_committed, result.commits);
```

## Stream query results

```rust theme={null}
use futures::StreamExt;

let mut batches = client
    .query_stream("SELECT id, total FROM analytics.orders")
    .await?;

while let Some(batch) = batches.next().await {
    let batch = batch?;
    println!("received {} rows", batch.num_rows());
}
```

The SDK decodes the Arrow IPC response incrementally instead of buffering the full result.

## Follow table commits

```rust theme={null}
use futures::StreamExt;

let mut changes = client.follow(["analytics.orders"], None)?;
while let Some(change) = changes.next().await {
    let change = change?;
    println!("{} committed snapshot {}", change.table, change.snapshot_id);
}
```

The stream reconnects after a socket drop and resumes from the last observed sequence. An expired replay cursor returns a distinct `ClientError::CursorExpired` error.

## Implement the worker contract

Implement `Worker<C>` for native in-process workers. The runtime passes a `WorkerContext<C>` with the trigger, outputs, environment, logger, abort flag, and memory-grant host.

The Rust trigger and result shapes match the TypeScript wire contract. Use `CronInterval` for logical time, `ChangeEvent` for data-change dispatches, and `WorkerResult` for the optional rows-written summary.
