# `#[api_sender]`

应用于 `impl` 块的属性宏，自动为标记了 `#[api(...)]` 的方法生成 OneBot API 调用体。

---

## 功能概述

在 OneBot 协议实现中，API 方法通常有固定的调用模式：构造 JSON 参数 → 发送请求 → 解析响应。`#[api_sender]` 宏通过自动代码生成消除了这些样板代码。

配合方法上的 `#[api(...)]` 属性，可以：

- 自动将方法参数转换为 JSON 请求体
- 自动调用 `send_and_parse()` 发送请求并解析响应
- 支持覆盖默认的 action 名称
- 支持从响应中提取特定字段
- 支持参数名到 JSON 键名的映射
- 支持忽略响应体，仅校验请求成功与否

---

## 基本用法

### 1. 标记 `impl` 块

将 `#[api_sender]` 放在 `impl` 块上：

```rust
use onebot_api_macros::api_sender;

#[api_sender]
impl MyAPIClient {
    // 方法会自动生成 API 调用体
}
```

### 2. 在方法上使用 `#[api(...)]`

需要自动生成的方法上添加 `#[api(...)]` 属性：

```rust
#[api_sender]
impl MyAPIClient {
    #[api]
    pub async fn send_private_msg(&self, user_id: i64, message: String) -> Result<serde_json::Value, Error> {
        // 方法体会被自动生成，这里的代码会被替换
    }
}
```

实际生成的代码等价于：

```rust
impl MyAPIClient {
    pub async fn send_private_msg(&self, user_id: i64, message: String) -> Result<serde_json::Value, Error> {
        let params = serde_json::json!({
            "user_id": user_id,
            "message": message,
        });
        self.send_and_parse("send_private_msg", params).await
    }
}
```

---

## `#[api(...)]` 属性详解

### 基本属性（无参数）

```rust
#[api]
pub async fn method_name(&self, param1: Type1, param2: Type2) -> Result<T, E>;
```

- 方法名自动作为 API action 名称
- 所有参数自动序列化为 JSON 对象中的字段
- 键名与参数名相同

### `response = Type` — 指定响应类型

```rust
#[api(response = SendMsgResponse)]
pub async fn send_group_msg(&self, group_id: i64, message: String) -> Result<SendMsgResponse, Error>;
```

生成的代码：

```rust
let params = serde_json::json!({
    "group_id": group_id,
    "message": message,
});
self.send_and_parse::<SendMsgResponse>("send_group_msg", params).await
```

### `extract = "field"` — 提取响应字段

如果 API 返回的响应是一个对象，而你只需要其中某个字段，可以使用 `extract`：

```rust
#[api(response = SendMsgResponse, extract = "message_id")]
pub async fn send_group_msg(&self, group_id: i64, message: String) -> Result<i64, Error>;
```

生成的代码：

```rust
let params = serde_json::json!({
    "group_id": group_id,
    "message": message,
});
let response: SendMsgResponse = self.send_and_parse("send_group_msg", params).await?;
Ok(response.message_id)
```

> **注意**：使用 `extract` 时必须同时指定 `response`，因为需要先反序列化为中间类型才能提取字段。

### `action = "name"` — 覆盖默认 action 名

默认情况下，方法名会直接作为 API action 名发送。若真实 action 与 Rust 方法名不一致，可以显式覆盖：

```rust
#[api(action = "ArkSharePeer")]
pub async fn ark_share_peer(&self, user_id: i64) -> Result<serde_json::Value, Error>;
```

生成的调用会使用 `"ArkSharePeer"`，而不是 `"ark_share_peer"`。

### `map(...)` — 参数名映射

OneBot API 的 JSON 键名可能与 Rust 参数命名风格不同（如蛇形命名 vs API 命名）。使用 `map` 可以将参数映射到不同的 JSON 键名：

```rust
#[api(map(group_id = "groupId", message = "msg"))]
pub async fn send_group_msg(&self, group_id: i64, message: String) -> Result<serde_json::Value, Error>;
```

生成的 JSON：

```json
{
    "groupId": group_id,
    "msg": message
}
```

`map` 可以与其他属性组合使用：

```rust
#[api(
    response = SendMsgResponse,
    extract = "message_id",
    map(user_id = "userId", message = "msg_content")
)]
pub async fn send_private_msg(&self, user_id: i64, message: String) -> Result<i64, Error>;
```

### `discard = true` — 忽略响应体

当接口只关心请求是否成功，不关心返回的 `data` 内容时，可以使用：

```rust
#[api(discard = true)]
pub async fn set_group_sign(&self, group_id: i64) -> Result<(), APIError>;
```

生成的代码会先发送请求并解析为 `serde_json::Value` 校验成功，再返回 `Ok(())`。

---

## 完整示例

```rust
use onebot_api_macros::api_sender;
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct SendMsgResponse {
    message_id: i64,
}

struct APIClient;

impl APIClient {
    async fn send_and_parse<T: serde::de::DeserializeOwned>(
        &self,
        action: &str,
        params: serde_json::Value,
    ) -> Result<T, APIError> {
        // 实际的网络请求逻辑
        todo!()
    }
}

#[api_sender]
impl APIClient {
    // 简单调用
    #[api]
    pub async fn get_login_info(&self) -> Result<serde_json::Value, APIError>;

    // 带参数映射
    #[api(map(user_id = "user_id"))]
    pub async fn get_stranger_info(&self, user_id: i64, no_cache: bool) -> Result<serde_json::Value, APIError>;

    // 带响应类型和字段提取
    #[api(response = SendMsgResponse, extract = "message_id")]
    pub async fn send_private_msg(&self, user_id: i64, message: String) -> Result<i64, APIError>;

    // 组合使用
    #[api(
        response = SendMsgResponse,
        extract = "message_id",
        map(group_id = "group_id", message = "message")
    )]
    pub async fn send_group_msg(&self, group_id: i64, message: String) -> Result<i64, APIError>;

    // 没有 #[api] 属性的方法保持不变
    pub async fn custom_method(&self) {
        // 手动实现
    }
}
```

---

## 工作原理

1. `#[api_sender]` 解析 `impl` 块中的所有方法
2. 检查每个方法是否有 `#[api(...)]` 属性
3. 对于有 `#[api]` 的方法：
   - 默认使用方法名作为 API action 名称，必要时由 `action = "..."` 覆盖
   - 扫描方法参数，生成 `serde_json::json!` 构造
   - 应用 `map` 重命名规则
   - 如果有 `discard = true`，发送请求后忽略响应体，仅保留成功/失败语义
   - 如果有 `extract`，先反序列化为 `response` 类型，再提取字段
   - 替换原方法体为生成的代码
4. 没有 `#[api]` 的方法原样保留

---

## 注意事项

- 目标类型必须有 `send_and_parse` 方法，签名大致为：
  ```rust
  async fn send_and_parse<T>(&self, action: &str, params: serde_json::Value) -> Result<T, E>
  ```
- `#[api]` 属性只能用于异步方法（`async fn`）
- 第一个参数必须是 `&self`（目前不支持 `&mut self`）
- `extract` 和 `response` 必须成对使用
- `discard = true` 时会忽略返回 `data` 的结构，仅校验请求成功
- `map` 中的条目用逗号分隔，格式为 `param_name = "json_key"`
