# cnb
[](https://crates.io/crates/cnb)
[](https://docs.rs/cnb)
[](https://opensource.org/licenses/MIT)
[](https://www.rust-lang.org)
> 非官方 Rust SDK,覆盖 [CNB (Cloud Native Build)](https://cnb.cool) 平台
> 公开的 [OpenAPI](https://api.cnb.cool) 全部 **241** 个端点(28 个资源
> 模块、271 个数据模型)。**0.2.0 是破坏性升级**——请阅读
> [CHANGELOG](./CHANGELOG.md) 中的迁移指南。
## 特性
- 🔐 Bearer Token 鉴权 + `CNB_TOKEN` 环境变量自动回退
- 🧱 全部端点类型化(请求 DTO + 响应 struct,仅 spec 缺失 schema 时降级为 `serde_json::Value`)
- 🧰 查询参数 Builder(告别 `Some(1), None, None, None, None…`)
- 💥 结构化错误:保留状态码、原始响应体、`code` / `message` / `request_id`
- 🔁 幂等方法的指数退避重试,自动尊重 `429 Retry-After`
- ⚡ 完全异步(基于 `tokio` + `reqwest 0.12`),TLS 默认 `rustls`
- 📊 可选 `tracing`:记录每次调用的 method / path / status / 耗时(绝不打印 token)
- 📈 可选 `stream`:通用分页 helper,把 `list_*` 端点变成 `Stream`
- 🚦 资源模块按 feature 拆分,按需开启
## 安装
```toml
[dependencies]
cnb = "0.2"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```
最小依赖(仅启用需要的资源):
```toml
[dependencies]
cnb = { version = "0.2", default-features = false,
features = ["rustls-tls", "retry", "repositories", "issues"] }
```
## 快速开始
```rust
use cnb::ApiClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 自动读取 CNB_TOKEN 环境变量。
let client = ApiClient::new()?;
// 当前用户信息(类型化响应)。
let me = client.users().get_user_info().await?;
println!("{me:#?}");
// 仓库详情(路径参数 + 类型化响应)。
let repo = client.repositories().get_by_id("owner/repo".into()).await?;
println!("{repo:#?}");
Ok(())
}
```
## 鉴权
| 1 | `ApiClient::builder().token("ghp_xxx").build()?` |
| 2 | 环境变量 `CNB_TOKEN` (默认行为) |
| 3 | 匿名(`ApiClient::builder().no_token().build()?` 或环境无 token)|
```rust
let client = cnb::ApiClient::builder()
.token("ghp_xxx") // 显式 token,优先级最高
.timeout(std::time::Duration::from_secs(15))
.user_agent("my-app/1.0")
.build()?;
```
## 调用风格:类型化 + Builder
每个查询参数被打包成对应的 `XxxQuery` Builder,配合类型化响应:
```rust
use cnb::repositories::GetReposQuery;
let query = GetReposQuery::new()
.page(1i64)
.page_size(20i64);
let repos: Vec<cnb::models::Repos4User> =
client.repositories().get_repos(&query).await?;
```
写请求体使用类型化 DTO:
```rust
use cnb::models::PostIssueForm;
let body = PostIssueForm {
title: Some("Bug: foo bar".into()),
body: Some("Reproduction steps:".into()),
..PostIssueForm::default()
};
let issue = client.issues().create_issue("owner/repo".into(), &body).await?;
```
## 错误处理
```rust
use cnb::{ApiClient, ApiError};
match client.repositories().get_by_id("does/not-exist".into()).await {
Ok(repo) => println!("{repo:#?}"),
Err(ApiError::Http { status: 404, request_id, .. }) => {
eprintln!("not found (request_id={request_id:?})");
}
Err(ApiError::Http { status, code, message, body, .. }) => {
eprintln!("HTTP {status} ({code:?}): {message}\nraw: {body}");
}
Err(ApiError::Reqwest(e)) => eprintln!("transport: {e}"),
Err(e) => eprintln!("other: {e}"),
}
```
## 重试
默认对 GET/HEAD/PUT/DELETE/OPTIONS 的 5xx / 408 / 429 / 网络错误进行最多 **3 次**
指数退避重试,并尊重 `Retry-After`。需要自定义:
```rust
use cnb::{ApiClient, RetryConfig};
use std::time::Duration;
let client = ApiClient::builder()
.retry(RetryConfig {
max_attempts: 5,
base_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(10),
})
.build()?;
```
## 分页(`stream` feature)
```toml
cnb = { version = "0.2", features = ["stream"] }
```
```rust
use cnb::{pagination::paginate, repositories::GetReposQuery, ApiClient};
use futures::StreamExt;
let client = ApiClient::new()?;
let repos = client.repositories();
async move {
let q = GetReposQuery::new().page(page).page_size(page_size);
repos.get_repos(&q).await
}
});
futures::pin_mut!(stream);
while let Some(repo) = stream.next().await {
let repo = repo?;
println!("{repo:?}");
}
```
## Tracing(`tracing` feature)
```toml
cnb = { version = "0.2", features = ["tracing"] }
```
```rust
tracing_subscriber::fmt()
.with_env_filter("cnb=debug")
.init();
let client = cnb::ApiClient::new()?;
let _ = client.users().get_user_info().await?;
// DEBUG cnb api call method=GET path=/user status=200 elapsed_ms=87
```
> Token 永远不会出现在日志里——`Authorization` 头被标记为 sensitive。
## Cargo Features
| `rustls-tls` | ✓ | 使用 `rustls` 作为 TLS 后端(无需系统 OpenSSL) |
| `native-tls` | | 使用系统 TLS(OpenSSL / Secure Transport) |
| `retry` | ✓ | 启用自动重试逻辑 |
| `tracing` | | 启用 `tracing` instrumentation |
| `stream` | | 启用 `pagination::paginate` 与 `Stream` adaptors |
| `all-resources` | ✓ | 启用所有资源模块 |
| `<resource>` | ✓ | 单独启用某个资源(`repositories`、`issues`、`git` …) |
`rustls-tls` 与 `native-tls` 互斥,需要切换时设置 `default-features = false`。
## 资源模块速览
`activities` · `ai` · `artifactory` · `assets` · `badge` · `build` · `charge` ·
`event` · `followers` · `git` · `git_settings` · `issues` · `knowledge_base` ·
`members` · `missions` · `organizations` · `pulls` · `rank` · `registries` ·
`releases` · `repo_code_issue` · `repo_contributor` · `repo_labels` ·
`repositories` · `search` · `security` · `starring` · `users` · `wiki` ·
`workspace`
完整方法签名见 [docs.rs/cnb](https://docs.rs/cnb)。
## 示例
`examples/` 目录提供 7 个可运行示例:
```bash
CNB_TOKEN=xxx cargo run --example auth_with_env
CNB_TOKEN=xxx cargo run --example list_repos
CNB_TOKEN=xxx cargo run --example create_issue -- owner/name
CNB_TOKEN=xxx cargo run --example git_branches -- owner/name
CNB_TOKEN=xxx cargo run --example error_handling
CNB_TOKEN=xxx RUST_LOG=cnb=debug \
cargo run --features tracing --example tracing_setup
CNB_TOKEN=xxx cargo run --features stream --example pulls_pagination -- owner/name 1
```
## 测试
集成测试基于 [`wiremock`](https://docs.rs/wiremock),覆盖鉴权、错误体解析、
重试、`Retry-After`、默认头、典型资源 happy path:
```bash
cargo test --all-features
```
## 重新生成 SDK
整个 SDK(`src/models/data.rs` + 28 个资源模块)由 `codegen/cnb-codegen` 工具
从 [`codegen/spec/swagger.json`](./codegen/spec/swagger.json) 生成。
当上游 spec 更新时:
```bash
curl -sS -o codegen/spec/swagger.json https://api.cnb.cool/swagger.json
cargo run --manifest-path codegen/Cargo.toml -- \
--spec codegen/spec/swagger.json --out src
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
```
详见 [`codegen/README.md`](./codegen/README.md)。
## MSRV
最低支持 Rust **1.75**。CI 在 stable + 1.75 双轨道上运行。
## 升级到 0.2.0
0.2.0 不向后兼容 0.1.x。完整迁移指南见 [CHANGELOG.md](./CHANGELOG.md)。
## 许可证
[MIT](./LICENSE)
## 相关链接
- [CNB 官网](https://cnb.cool) | [API 文档](https://api.cnb.cool) | [使用文档](https://docs.cnb.cool)
- [docs.rs/cnb](https://docs.rs/cnb)
- [Issues / PRs](https://cnb.cool/aodoo/tools/rust-cnb)