This commit is contained in:
Sam Samai 2021-08-17 22:37:28 +10:00
parent 53bbf32229
commit 73b72e3a31
7 changed files with 175 additions and 105 deletions

Binary file not shown.

View File

@ -1,90 +0,0 @@
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>, post: Json<Post>) -> Result<Created<Json<Post>>> {
// There is no support for `RETURNING`.
sqlx::query!("INSERT INTO posts (title, text) VALUES (?, ?)", post.title, post.text)
.execute(&mut *db)
.await?;
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])
})
}

View File

@ -0,0 +1,113 @@
use rocket::fairing::{self, AdHoc};
use rocket::response::status::Created;
use rocket::serde::{json::Json, Deserialize, Serialize};
use rocket::{futures, Build, Rocket};
use rocket_db_pools::{sqlx, Connection, Database};
use futures::{future::TryFutureExt, stream::TryStreamExt};
// use post::*;
mod post;
pub use post::Entity as Post;
#[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>, post: Json<Post>) -> Result<Created<Json<Post>>> {
// // There is no support for `RETURNING`.
// sqlx::query!(
// "INSERT INTO posts (title, text) VALUES (?, ?)",
// post.title,
// post.text
// )
// .execute(&mut *db)
// .await?;
// 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?;
let ids = vec![];
Ok(Json(ids))
}
#[get("/<id>")]
async fn read(mut db: Connection<Db>, id: i64) -> Option<Json<Post>> {
let post: Option<post::Model> = Post::find_by_id(id)
.one(db)
.await
.expect("could not find baker");
println!("post: {:#?}", 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()
None
}
// #[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, read])
})
}

View File

@ -0,0 +1,51 @@
use rocket::serde::{json::Json, Deserialize, Serialize};
use sea_orm::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"post"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Model {
pub id: Option<i64>,
pub title: String,
pub text: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
Id,
Title,
Text,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
Id,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
true
}
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Title => ColumnType::String(None).def(),
Self::Text => ColumnType::String(None).def(),
}
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -1,7 +1,7 @@
use rocket::fairing::AdHoc; use rocket::fairing::AdHoc;
use rocket::local::blocking::Client;
use rocket::serde::{Serialize, Deserialize};
use rocket::http::Status; use rocket::http::Status;
use rocket::local::blocking::Client;
use rocket::serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")] #[serde(crate = "rocket::serde")]
@ -20,13 +20,19 @@ fn test(base: &str, stage: AdHoc) {
// Clear everything from the database. // Clear everything from the database.
assert_eq!(client.delete(base).dispatch().status(), Status::Ok); assert_eq!(client.delete(base).dispatch().status(), Status::Ok);
assert_eq!(client.get(base).dispatch().into_json::<Vec<i64>>(), Some(vec![])); assert_eq!(
client.get(base).dispatch().into_json::<Vec<i64>>(),
Some(vec![])
);
// Add some random posts, ensure they're listable and readable. // Add some random posts, ensure they're listable and readable.
for i in 1..=N{ for i in 1..=N {
let title = format!("My Post - {}", i); let title = format!("My Post - {}", i);
let text = format!("Once upon a time, at {}'o clock...", i); let text = format!("Once upon a time, at {}'o clock...", i);
let post = Post { title: title.clone(), text: text.clone() }; let post = Post {
title: title.clone(),
text: text.clone(),
};
// Create a new post. // Create a new post.
let response = client.post(base).json(&post).dispatch().into_json::<Post>(); let response = client.post(base).json(&post).dispatch().into_json::<Post>();
@ -66,13 +72,3 @@ fn test(base: &str, stage: AdHoc) {
fn test_sqlx() { fn test_sqlx() {
test("/sqlx", crate::sqlx::stage()) test("/sqlx", crate::sqlx::stage())
} }
#[test]
fn test_diesel() {
test("/diesel", crate::diesel_sqlite::stage())
}
#[test]
fn test_rusqlite() {
test("/rusqlite", crate::rusqlite::stage())
}