1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/*!
# axum-mongodb

[![GitHub Stars](https://img.shields.io/github/stars/yexiyue/axum-mongodb?style=flat-square)](https://github.com/yexiyue/axum-mongodb)
[![Crates.io](https://img.shields.io/crates/v/axum-mongodb?style=flat-square)](https://crates.io/crates/axum-mongodb)

**axum-mongodb** 是一个为 [Axum](https://github.com/tokio-rs/axum) Web 框架量身打造的库,旨在提供一种简洁且优雅的 MongoDB 集成方案。本库的设计灵感来源于著名的 JavaScript 框架 [Nest.js](https://nestjs.com/),致力于简化并提升 Axum 项目中对 MongoDB 数据库的操作效率。

### 功能亮点
- **基于状态管理的数据库连接**
- **便捷的 CRUD 操作封装**

### 安装

在 `Cargo.toml` 中添加 axum-mongodb 依赖:

```toml
[dependencies]
axum-mongodb = "1.0.3"
```

或者通过 `cargo add` 命令快速安装:

```bash
cargo add axum-mongodb
```

### 使用教程

#### 1. 初始化数据库连接

在项目的入口点(如 `lib.rs`)中使用 `axum_mongodb::main` 属性宏来设置 MongoDB 连接和初始化数据库服务。

```rust
use anyhow::Result;
use axum::{response::IntoResponse, routing::get, Router};
use axum_mongodb::preload::*;
use mongodb::{options::ClientOptions, Client};
use tokio::net::TcpListener;

// ...

#[axum_mongodb::main]
pub async fn start() -> Result<()> {
    // 解析并创建 MongoDB 客户端配置
    let client_options = ClientOptions::parse("mongodb://mongodb:password@localhost:21045/admin").await?;
    let client = Client::with_options(client_options)?;
    let db = client.database("todo");

    // 创建 MongoDB 服务器状态实例
    let mongodb_server = MongoDbServer::<Servers>::new(db).await;

    // 构建 Axum 应用,并注入 MongoDB 状态到全局路由
    let app = Router::new()
        .route("/", get(hello_world))
        .merge(todos_router())
        .with_state(mongodb_server);

    // 启动服务器监听
    let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
    tracing::info!("Listening on http://{}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();

    Ok(())
}

async fn hello_world() -> impl IntoResponse {
    "hello world"
}
```

#### 2. 定义数据模型

利用 `axum_mongodb::Column` Derive 宏装饰你的结构体以支持与 MongoDB 的交互:

```rust
use anyhow::Result;
use axum_mongodb::futures::TryStreamExt;
use bson::{self, doc, oid::ObjectId};
use mongodb::{results::{DeleteResult, InsertOneResult, UpdateResult},};
use serde::{Deserialize, Serialize};

#[derive(Debug, Column, Deserialize, Serialize, Clone)]
pub struct Todo {
    #[serde(
        serialize_with = "bson::serde_helpers::serialize_object_id_as_hex_string",
        rename = "_id"
    )]
    id: ObjectId,
    description: String,
    completed: bool,
    create_time: chrono::DateTime<chrono::Local>,
    update_time: chrono::Local,
}

impl Server<Todo> {
    pub async fn create_todo(&self, description: String) -> Result<InsertOneResult> {
        Ok(self
            .insert_one(
                Todo {
                    id: ObjectId::new(),
                    description,
                    completed: false,
                    create_time: chrono::Local::now(),
                    update_time: chrono::Local::now(),
                },
                None,
            )
            .await?)
    }

    // ... 其他CRUD方法实现
}
```

#### 3. 在 Axum handler 中使用

在处理函数中注入 `Server<Todo>` 实例,并调用相应的方法完成数据库操作:

```rust
use axum::{extract::Path, response::IntoResponse, Json};
use serde::Deserialize;
use super::Todo;
use crate::Server;

#[derive(Debug, Deserialize)]
pub struct TodoQuery {
    pub description: String,
    pub completed: Option<bool>,
}

pub async fn create_todo(todo: Server<Todo>, Json(TodoQuery { description, .. }): Json<TodoQuery>) -> impl IntoResponse {
    let res = todo.create_todo(description).await.unwrap();
    Json(res)
}
```

#### 4. 注册路由

定义并组合相关路由,将 MongoDB 服务状态注入到路由模块中:

```rust
mod controller;
use controller::{create_todo, delete_todo, get_todo, get_todos, update_todo};
use axum::{
    routing::{get, post},
    Router,
};
use axum_mongodb::MongoDbServer;

pub use server::Todo;

pub fn todos_router() -> Router<MongoDbServer<Servers>> {
    Router::new()
        .route("/todos", post(create_todo).get(get_todos))
        .route("/todos/:id", get(get_todo).put(update_todo).delete(delete_todo))
}

```

### 示例代码与文档

完整的示例代码可参考 [axum-mongodb-example](https://github.com/yexiyue/axum-mongodb/blob/master/examples/axum/src/lib.rs)。同时,你可以查阅 [API 文档](https://apifox.com/apidoc/shared-6bef1065-5c3e-42a8-bf10-73e21f671fe1) 以获得更详细的信息和示例说明。

### 更多信息

请访问项目主页或查看仓库中的文档以获取更多关于如何在您的 Axum 项目中高效地集成和使用 MongoDB 的细节及高级功能。
 */
#[doc(hidden)]
pub use axum::async_trait;

pub use axum_mongodb_core::{inject, main, Column};

#[doc(hidden)]
pub use axum_mongodb_core::inject_meta;
#[doc(hidden)]
pub use futures;
#[doc(hidden)]
pub use mongodb;

mod mongodb_server;
pub use mongodb_server::MongoDbServer;

pub mod preload {
    //! 重新导出常用的结构体和宏

    #[doc(hidden)]
    pub use crate::CollectionInit;
    pub use crate::MongoDbServer;
    #[doc(hidden)]
    pub use crate::NewWithDb;
    pub use axum_mongodb_core::{inject, main, Column};
    pub struct DBServers;
}

#[doc(hidden)]
#[async_trait]
pub trait NewWithDb {
    async fn new(db: mongodb::Database) -> Self;
}

#[doc(hidden)]
#[async_trait]
pub trait CollectionInit {
    async fn init(&self);
}

#[doc(hidden)]
pub struct CreateIndexOptions {
    pub keys: mongodb::bson::Document,
    pub unique: bool,
    pub name: Option<String>,
}