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
214
215
216
217
218
219
220
/*!
## axum-mongodb

本库旨在为Axum项目提供一种更为简洁且优雅的MongoDB集成方案,其设计灵感来源于知名框架[Nest.js](https://nestjs.com/)。通过使用本库,开发者能够在Axum项目中实现对MongoDB数据库的高度简化及高效利用。

### 使用方式

#### 1.安装依赖

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

#### 2.在入口函数使用main属性宏

**lib.rs**

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

use crate::todos::todos_router;

// 在lib中使用,这样生成的结构体才能在整个项目中使用
#[axum_mongodb::main]
pub async fn start() -> Result<()> {
    let client_options =
        ClientOptions::parse("mongodb://mongodb:password@localhost:21045/admin").await?;
    let client = Client::with_options(client_options)?;
    let db = client.database("todo");

    // 定义State(关键代码)
    let mongodb_server = MongoDbServer::<Servers>::new(db).await;

    let app = Router::new()
        .route("/", get(hello_world))
        .merge(todos_router())
        // 注册State
        .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"
}
```

**main.rs**

```rust
use anyhow::Result;
#[tokio::main]
async fn main() -> Result<()> {
    axum_example::start().await?;
    Ok(())
}
```

#### 3.在结构体上使用Column Derive宏

```rust
use crate::Server;
use anyhow::Result;
use axum_mongodb::futures::TryStreamExt;
// 导入axum-mongodb
use axum_mongodb::preload::*;
use mongodb::{
    bson::{self, doc, oid::ObjectId},
    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::DateTime<chrono::Local>,
}

// 实现相应方法,在handler中可以调用到
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?)
    }
		// ...
}

```

#### 4.在axum handler中使用

```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)
}

// ...
```

#### 5.注册路由

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

use crate::Servers;

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)

 */
#[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>,
}