rune-framework 1.1.1

Official Rust SDK for Rune — protocol-first multi-language function execution framework
Documentation
# Rune Rust SDK

Official Rust Caster SDK for the Rune framework.

## 依赖

```toml
[dependencies]
rune-framework = { path = "sdks/rust" }
tokio = { version = "1", features = ["full"] }
bytes = "1"
```

## 快速开始

```rust
use rune_framework::{Caster, CasterConfig, RuneConfig, RuneContext, GateConfig};
use bytes::Bytes;

#[tokio::main]
async fn main() {
    let caster = Caster::new(CasterConfig::default());

    caster.rune(
        RuneConfig {
            name: "echo".into(),
            gate: Some(GateConfig::new("/echo")),
            ..Default::default()
        },
        |_ctx: RuneContext, input: Bytes| async move { Ok(input) },
    ).unwrap();

    caster.run().await.unwrap();
}
```

启动后 Runtime 自动暴露:
```
POST /echo              -- sync
POST /echo?stream=true  -- SSE streaming
POST /echo?async=true   -- async task
```

## Caster 配置

```rust
use std::collections::HashMap;

let config = CasterConfig {
    runtime: "localhost:50070".into(),      // Runtime gRPC 地址
    key: Some("rk_xxx".into()),            // API Key
    caster_id: Some("my-rust-caster".into()), // Caster ID(默认自动生成 UUID)
    max_concurrent: 10,                     // 最大并发处理数
    labels: HashMap::from([                 // Caster 标签
        ("env".into(), "prod".into()),
        ("gpu".into(), "true".into()),
    ]),
    heartbeat_interval_secs: 10.0,         // 心跳间隔(秒)
    reconnect_base_delay_secs: 1.0,        // 重连初始延迟(秒)
    reconnect_max_delay_secs: 30.0,        // 重连最大延迟(秒)
};

let caster = Caster::new(config);
```

## 注册 Rune

### Unary Handler

```rust
use rune_framework::{RuneConfig, GateConfig};

caster.rune(
    RuneConfig {
        name: "translate".into(),
        version: "1.0.0".into(),
        description: "Translate text".into(),
        gate: Some(GateConfig::new("/translate")),
        priority: 10,
        input_schema: Some(serde_json::json!({
            "type": "object",
            "properties": {
                "text": {"type": "string"},
                "lang": {"type": "string"}
            },
            "required": ["text", "lang"]
        })),
        output_schema: Some(serde_json::json!({
            "type": "object",
            "properties": {
                "translated": {"type": "string"}
            }
        })),
        ..Default::default()
    },
    |_ctx, input| async move {
        let data: serde_json::Value = serde_json::from_slice(&input)?;
        let result = serde_json::json!({"translated": "hello"});
        Ok(Bytes::from(serde_json::to_vec(&result)?))
    },
).unwrap();
```

### Unary Handler with Files

```rust
use rune_framework::FileAttachment;

caster.rune_with_files(
    RuneConfig {
        name: "process-doc".into(),
        gate: Some(GateConfig::new("/process")),
        ..Default::default()
    },
    |_ctx, input, files: Vec<FileAttachment>| async move {
        for f in &files {
            println!("{} {} {}", f.filename, f.mime_type, f.data.len());
        }
        let result = serde_json::json!({"processed": files.len()});
        Ok(Bytes::from(serde_json::to_vec(&result)?))
    },
).unwrap();
```

### Streaming Handler

```rust
use rune_framework::StreamSender;

caster.stream_rune(
    RuneConfig {
        name: "generate".into(),
        gate: Some(GateConfig::new("/generate")),
        ..Default::default()
    },
    |_ctx, _input, sender: StreamSender| async move {
        for i in 0..10 {
            sender.send(Bytes::from(format!("chunk {}", i))).await?;
        }
        Ok(())
    },
).unwrap();
```

### Streaming Handler with Files

```rust
caster.stream_rune_with_files(
    RuneConfig {
        name: "stream-process".into(),
        gate: Some(GateConfig::new("/stream-process")),
        ..Default::default()
    },
    |_ctx, _input, files: Vec<FileAttachment>, sender: StreamSender| async move {
        for f in &files {
            sender.send(Bytes::from(format!("processing {}", f.filename))).await?;
        }
        sender.send(Bytes::from("done")).await?;
        Ok(())
    },
).unwrap();
```

## Context

每次调用都会传入 `RuneContext`:

```rust
caster.rune(
    RuneConfig::new("example"),
    |ctx: RuneContext, input: Bytes| async move {
        println!("rune: {}", ctx.rune_name);
        println!("request: {}", ctx.request_id);
        println!("context: {:?}", ctx.context);

        // 取消感知
        if ctx.cancellation.is_cancelled() {
            return Err(rune_framework::SdkError::Cancelled);
        }

        Ok(input)
    },
).unwrap();
```

`ctx.cancellation` 是 `tokio_util::sync::CancellationToken`,可用于检测取消:

```rust
tokio::select! {
    result = do_long_work() => { Ok(result?) }
    _ = ctx.cancellation.cancelled() => {
        Err(rune_framework::SdkError::Cancelled)
    }
}
```

## 错误处理

SDK 使用 `SdkResult<T>` 类型(`Result<T, SdkError>`):

```rust
use rune_framework::{SdkError, SdkResult};

// SdkError variants:
// - DuplicateRune(String)   -- 重复注册
// - InvalidUri(String)      -- 无效 URI
// - ChannelSend(String)     -- 通道发送失败
// - Cancelled               -- 请求被取消
// - Transport(...)          -- gRPC 传输错误
```

## 运行与查询

```rust
// 阻塞运行(自动重连)
caster.run().await?;

// 查询状态
println!("Rune count: {}", caster.rune_count());
println!("Caster ID: {}", caster.caster_id());
println!("Is stream: {}", caster.is_stream_rune("generate"));
println!("Accepts files: {}", caster.rune_accepts_files("process-doc"));
```

## 开发

```bash
cd sdks/rust
cargo test                        # 单元测试
cargo test --test e2e_test        # E2E 测试(需要运行中的 Runtime)
```