artisan 0.11.0

Api RequesT Framework U Like - 你喜欢的 Rust API 请求框架
Documentation
# Artisan

> Api RequesT Framework U Like - 你喜欢的 Rust API 请求框架

基于洋葱模型的 Rust HTTP 客户端框架,灵感来自 [yansongda/artful](https://github.com/yansongda/artful)。

## 特性

- 🔄 **洋葱模型**: 请求层层穿透,响应层层返回
- 🔌 **插件化**: 每个请求都是一个插件组合,高度灵活可定制
- 🛡️ **类型安全**: Rust 类型系统确保配置和参数的类型安全
-**高性能**: 全局 HTTP Client 单例,共享连接池
- 📦 **零依赖冲突**: 仅使用主流稳定依赖

## 安装

```toml
[dependencies]
artisan = "~0.11"
```

> 使用 `~` 版本符号确保依赖兼容性,`~0.11` 表示兼容 0.11.x 的所有版本。

## 快速开始

### 初始化框架

```rust
use artisan::{Artful, Config};

// 初始化框架配置(可选)
// 首次调用成功返回 true,后续调用返回 false(OnceLock 不支持覆盖)
Artful::config(Config::default());
```

### 基础使用

```rust
use artisan::{Artful, Plugin, Rocket, flow_ctrl::Next};
use artisan::plugins::{StartPlugin, AddPayloadBodyPlugin, AddRadarPlugin, ParserPlugin};
use async_trait::async_trait;
use std::sync::Arc;
use std::collections::HashMap;
use serde_json::json;

struct MethodUrlPlugin {
    method: reqwest::Method,
    url: String,
}

#[async_trait]
impl Plugin for MethodUrlPlugin {
    async fn assembly(&self, rocket: &mut Rocket, next: Next<'_>) -> artisan::Result<()> {
        rocket.config.method = self.method.clone();
        rocket.config.url = self.url.clone();
        next.call(rocket).await
    }
}

#[tokio::main]
async fn main() -> artisan::Result<()> {
    let params = HashMap::from([
        ("order_id", json!("123")),
        ("amount", json!(100)),
    ]);

    let plugins: Vec<Arc<dyn artisan::Plugin>> = vec![
        Arc::new(StartPlugin),
        Arc::new(MethodUrlPlugin {
            method: reqwest::Method::POST,
            url: "https://api.example.com/orders".to_string(),
        }),
        Arc::new(AddPayloadBodyPlugin),
        Arc::new(AddRadarPlugin),
        Arc::new(ParserPlugin),
    ];

    let result = Artful::artful(params, plugins).await?;
    
    if let artisan::Destination::Json(json) = result {
        println!("Response: {}", json);
    }

    Ok(())
}
```

### 使用 Shortcut 快捷方式

```rust
use artisan::{Artful, Shortcut, Plugin};
use artisan::plugins::{StartPlugin, AddPayloadBodyPlugin, AddRadarPlugin, ParserPlugin};
use std::sync::Arc;
use std::collections::HashMap;

#[derive(Default)]
struct MyApiShortcut {
    method: reqwest::Method,
    url: String,
}

impl Shortcut for MyApiShortcut {
    fn get_plugins(&self, _params: &HashMap<String, serde_json::Value>) 
        -> Vec<Arc<dyn Plugin>> 
    {
        vec![
            Arc::new(StartPlugin),
            Arc::new(MethodUrlPlugin {
                method: self.method.clone(),
                url: self.url.clone(),
            }),
            Arc::new(AddPayloadBodyPlugin),
            Arc::new(AddRadarPlugin),
            Arc::new(ParserPlugin),
        ]
    }
}

let shortcut = MyApiShortcut {
    method: reqwest::Method::POST,
    url: "https://api.example.com/orders".to_string(),
};
let result = Artful::shortcut(shortcut, HashMap::new()).await?;
```

### 自定义插件

```rust
use artisan::{Plugin, Rocket, flow_ctrl::Next};
use async_trait::async_trait;

pub struct SignaturePlugin {
    api_key: String,
}

#[async_trait]
impl Plugin for SignaturePlugin {
    async fn assembly(&self, rocket: &mut Rocket, next: Next<'_>) -> artisan::Result<()> {
        rocket.config.headers.insert(
            "X-Signature".to_string(),
            sign(&self.api_key, &rocket.payload),
        );
        
        next.call(rocket).await
    }
}
```

**错误处理**: 插件返回 `Result<()>`,任一插件失败会终止整个链并传播错误。

## 核心概念

### Rocket - 请求载体

`Rocket` 是整个请求生命周期中的数据载体:

```rust
pub struct Rocket {
    params: HashMap<String, Value>,   // 原始参数(不变)
    pub payload: HashMap<String, Value>, // 业务参数(可修改)
    pub config: RocketConfig,         // HTTP 配置(可修改)
    pub radar: Option<Request>,       // HTTP 请求对象
    pub destination: Option<Destination>, // 解析结果
    pub packer: Arc<dyn Packer>,      // 序列化器
}
```

**设计说明**:
- `params`: 原始参数,由调用方传入,整个生命周期中保持不变
- `payload`: 业务参数,由 `StartPlugin``params` 初始化,后续插件可修改
- `config`: HTTP 配置,包含 `direction`(响应解析策略),由插件负责设置

### RocketConfig - 请求配置

```rust
pub struct RocketConfig {
    pub method: reqwest::Method,
    pub url: String,
    pub headers: HashMap<String, String>,
    pub body: Option<String>,
    pub http: HttpOptions,
    pub direction: DirectionKind,     // 响应解析策略
}
```

### Plugin - 插件(洋葱模型)

插件是洋葱模型的核心,每个插件可以在请求前向和后向阶段执行操作:

```rust
#[async_trait]
pub trait Plugin: Send + Sync + 'static {
    async fn assembly(&self, rocket: &mut Rocket, next: Next<'_>) -> Result<()>;
}
```

执行流程:
```
请求 → Plugin1 前向 → Plugin2 前向 → Plugin3 前向 → HTTP 请求
响应 ← Plugin1 后向 ← Plugin2 后向 ← Plugin3 后向 ← HTTP 响应
```

### Direction - 响应解析策略

```rust
pub enum DirectionKind {
    Json,             // 解析为 JSON(默认)
    Response,         // 返回原始 Response
    NoRequest,        // 不发起 HTTP 请求
    Custom(Arc<dyn Direction>), // 自定义解析器
}
```

## 内置插件

| 插件 | 功能 |
|------|------|
| `StartPlugin` | 将 params 初始化到 payload |
| `AddPayloadBodyPlugin` | 将 payload 序列化为请求体 |
| `AddRadarPlugin` | 构建 HTTP Request |
| `ParserPlugin` | 执行请求并解析响应 |

## 文档

详细架构设计请参阅 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。

## 许可证

MIT License