/
sqlx.rs
95 lines (79 loc) · 2.79 KB
/
sqlx.rs
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
use rocket::{Rocket, Build, futures};
use rocket::fairing::{self, AdHoc};
use rocket::response::status::Created;
use rocket::serde::{Serialize, Deserialize, json::Json};
use rocket_db_pools::{sqlx, Database, Connection};
use futures::{stream::TryStreamExt, future::TryFutureExt};
#[derive(Database)]
#[database("sqlx")]
struct Db(sqlx::SqlitePool);
type Result<T, E = rocket::response::Debug<sqlx::Error>> = std::result::Result<T, E>;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
struct Post {
#[serde(skip_deserializing, skip_serializing_if = "Option::is_none")]
id: Option<i64>,
title: String,
text: String,
}
#[post("/", data = "<post>")]
async fn create(mut db: Connection<Db>, mut post: Json<Post>) -> Result<Created<Json<Post>>> {
// NOTE: sqlx#2543, sqlx#1648 mean we can't use the pithier `fetch_one()`.
let results = sqlx::query!(
"INSERT INTO posts (title, text) VALUES (?, ?) RETURNING id",
post.title, post.text
)
.fetch(&mut **db)
.try_collect::<Vec<_>>()
.await?;
post.id = Some(results.first().expect("returning results").id);
Ok(Created::new("/").body(post))
}
#[get("/")]
async fn list(mut db: Connection<Db>) -> Result<Json<Vec<i64>>> {
let ids = sqlx::query!("SELECT id FROM posts")
.fetch(&mut **db)
.map_ok(|record| record.id)
.try_collect::<Vec<_>>()
.await?;
Ok(Json(ids))
}
#[get("/<id>")]
async fn read(mut db: Connection<Db>, id: i64) -> Option<Json<Post>> {
sqlx::query!("SELECT id, title, text FROM posts WHERE id = ?", id)
.fetch_one(&mut **db)
.map_ok(|r| Json(Post { id: Some(r.id), title: r.title, text: r.text }))
.await
.ok()
}
#[delete("/<id>")]
async fn delete(mut db: Connection<Db>, id: i64) -> Result<Option<()>> {
let result = sqlx::query!("DELETE FROM posts WHERE id = ?", id)
.execute(&mut **db)
.await?;
Ok((result.rows_affected() == 1).then(|| ()))
}
#[delete("/")]
async fn destroy(mut db: Connection<Db>) -> Result<()> {
sqlx::query!("DELETE FROM posts").execute(&mut **db).await?;
Ok(())
}
async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
match Db::fetch(&rocket) {
Some(db) => match sqlx::migrate!("db/sqlx/migrations").run(&**db).await {
Ok(_) => Ok(rocket),
Err(e) => {
error!("Failed to initialize SQLx database: {}", e);
Err(rocket)
}
}
None => Err(rocket),
}
}
pub fn stage() -> AdHoc {
AdHoc::on_ignite("SQLx Stage", |rocket| async {
rocket.attach(Db::init())
.attach(AdHoc::try_on_ignite("SQLx Migrations", run_migrations))
.mount("/sqlx", routes![list, create, read, delete, destroy])
})
}