Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
use rocket::{Rocket, State, Build, futures};
|
|
|
|
use rocket::fairing::{self, AdHoc};
|
2021-04-08 02:01:48 +00:00
|
|
|
use rocket::response::status::Created;
|
|
|
|
use rocket_contrib::json::Json;
|
|
|
|
|
|
|
|
use futures::stream::TryStreamExt;
|
|
|
|
use futures::future::TryFutureExt;
|
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
use sqlx::ConnectOptions;
|
|
|
|
|
|
|
|
type Db = sqlx::SqlitePool;
|
|
|
|
|
|
|
|
type Result<T, E = rocket::response::Debug<sqlx::Error>> = std::result::Result<T, E>;
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
|
|
struct Post {
|
|
|
|
#[serde(skip_deserializing, skip_serializing_if = "Option::is_none")]
|
|
|
|
id: Option<i64>,
|
|
|
|
title: String,
|
|
|
|
text: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/", data = "<post>")]
|
2021-05-11 13:56:35 +00:00
|
|
|
async fn create(db: &State<Db>, post: Json<Post>) -> Result<Created<Json<Post>>> {
|
2021-04-08 02:01:48 +00:00
|
|
|
// There is no support for `RETURNING`.
|
|
|
|
sqlx::query!("INSERT INTO posts (title, text) VALUES (?, ?)", post.title, post.text)
|
2021-05-11 13:56:35 +00:00
|
|
|
.execute(&**db)
|
2021-04-08 02:01:48 +00:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(Created::new("/").body(post))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/")]
|
2021-05-11 13:56:35 +00:00
|
|
|
async fn list(db: &State<Db>) -> Result<Json<Vec<i64>>> {
|
2021-04-08 02:01:48 +00:00
|
|
|
let ids = sqlx::query!("SELECT id FROM posts")
|
2021-05-11 13:56:35 +00:00
|
|
|
.fetch(&**db)
|
2021-04-08 02:01:48 +00:00
|
|
|
.map_ok(|record| record.id)
|
|
|
|
.try_collect::<Vec<_>>()
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(Json(ids))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/<id>")]
|
2021-05-11 13:56:35 +00:00
|
|
|
async fn read(db: &State<Db>, id: i64) -> Option<Json<Post>> {
|
2021-04-08 02:01:48 +00:00
|
|
|
sqlx::query!("SELECT id, title, text FROM posts WHERE id = ?", id)
|
2021-05-11 13:56:35 +00:00
|
|
|
.fetch_one(&**db)
|
2021-04-08 02:01:48 +00:00
|
|
|
.map_ok(|r| Json(Post { id: Some(r.id), title: r.title, text: r.text }))
|
|
|
|
.await
|
|
|
|
.ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[delete("/<id>")]
|
2021-05-11 13:56:35 +00:00
|
|
|
async fn delete(db: &State<Db>, id: i64) -> Result<Option<()>> {
|
2021-04-08 02:01:48 +00:00
|
|
|
let result = sqlx::query!("DELETE FROM posts WHERE id = ?", id)
|
2021-05-11 13:56:35 +00:00
|
|
|
.execute(&**db)
|
2021-04-08 02:01:48 +00:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok((result.rows_affected() == 1).then(|| ()))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[delete("/")]
|
2021-05-11 13:56:35 +00:00
|
|
|
async fn destroy(db: &State<Db>) -> Result<()> {
|
|
|
|
sqlx::query!("DELETE FROM posts").execute(&**db).await?;
|
2021-04-08 02:01:48 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
async fn init_db(rocket: Rocket<Build>) -> fairing::Result {
|
2021-04-08 02:01:48 +00:00
|
|
|
use rocket_contrib::databases::Config;
|
|
|
|
|
|
|
|
let config = match Config::from("sqlx", &rocket) {
|
|
|
|
Ok(config) => config,
|
|
|
|
Err(e) => {
|
|
|
|
error!("Failed to read SQLx config: {}", e);
|
|
|
|
return Err(rocket);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
|
|
|
.filename(&config.url)
|
|
|
|
.create_if_missing(true);
|
|
|
|
|
|
|
|
opts.disable_statement_logging();
|
|
|
|
let db = match Db::connect_with(opts).await {
|
|
|
|
Ok(db) => db,
|
|
|
|
Err(e) => {
|
|
|
|
error!("Failed to connect to SQLx database: {}", e);
|
|
|
|
return Err(rocket);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Err(e) = sqlx::migrate!("db/sqlx/migrations").run(&db).await {
|
|
|
|
error!("Failed to initialize SQLx database: {}", e);
|
|
|
|
return Err(rocket);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(rocket.manage(db))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stage() -> AdHoc {
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
AdHoc::on_ignite("SQLx Stage", |rocket| async {
|
2021-04-08 02:01:48 +00:00
|
|
|
rocket
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
.attach(AdHoc::try_on_ignite("SQLx Database", init_db))
|
2021-04-08 02:01:48 +00:00
|
|
|
.mount("/sqlx", routes![list, create, read, delete, destroy])
|
|
|
|
})
|
|
|
|
}
|