From 1da5c49dfb4b2a14508991a875fbe1b5e8587f74 Mon Sep 17 00:00:00 2001 From: Kilerd Chan Date: Fri, 14 Sep 2018 00:35:12 +0800 Subject: [PATCH] feat: migration and project structure --- Cargo.toml | 9 +++- Rocket.toml | 5 +-- migrations/.gitkeep | 0 .../down.sql | 6 +++ .../up.sql | 36 ++++++++++++++++ .../2018-09-13-162215_create_posts/down.sql | 2 + .../2018-09-13-162215_create_posts/up.sql | 7 ++++ src/main.rs | 22 +++++++++- src/pg_pool.rs | 42 +++++++++++++++++++ src/schema.rs | 8 ++++ 10 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 migrations/.gitkeep create mode 100644 migrations/00000000000000_diesel_initial_setup/down.sql create mode 100644 migrations/00000000000000_diesel_initial_setup/up.sql create mode 100644 migrations/2018-09-13-162215_create_posts/down.sql create mode 100644 migrations/2018-09-13-162215_create_posts/up.sql create mode 100644 src/pg_pool.rs create mode 100644 src/schema.rs diff --git a/Cargo.toml b/Cargo.toml index cee983f..3bafeb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,11 @@ license = "MIT" [dependencies] rocket = "0.3.16" -rocket_codegen = "0.3.16" \ No newline at end of file +rocket_codegen = "0.3.16" +rocket_contrib = "0.3" +diesel = { version = "1.0.0", features = ["postgres", "r2d2"] } +dotenv = "0.9.0" +r2d2 = "0.8" +lazy_static = "1.1" +serde = "1.0" +serde_derive = "1.0" \ No newline at end of file diff --git a/Rocket.toml b/Rocket.toml index 2162549..c8034cc 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -1,5 +1,2 @@ [global] -template_dir = "static" - -[global.databases.sqlite_database] -url = "db/db.sqlite" \ No newline at end of file +template_dir = "static" \ No newline at end of file diff --git a/migrations/.gitkeep b/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/migrations/00000000000000_diesel_initial_setup/down.sql b/migrations/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 0000000..a9f5260 --- /dev/null +++ b/migrations/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,6 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/migrations/00000000000000_diesel_initial_setup/up.sql b/migrations/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 0000000..d68895b --- /dev/null +++ b/migrations/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/migrations/2018-09-13-162215_create_posts/down.sql b/migrations/2018-09-13-162215_create_posts/down.sql new file mode 100644 index 0000000..e00da65 --- /dev/null +++ b/migrations/2018-09-13-162215_create_posts/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE posts \ No newline at end of file diff --git a/migrations/2018-09-13-162215_create_posts/up.sql b/migrations/2018-09-13-162215_create_posts/up.sql new file mode 100644 index 0000000..7208738 --- /dev/null +++ b/migrations/2018-09-13-162215_create_posts/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + title VARCHAR NOT NULL, + body TEXT NOT NULL, + published BOOLEAN NOT NULL DEFAULT 'f' +) \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index cee3770..109071a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,20 @@ #![feature(plugin)] #![plugin(rocket_codegen)] extern crate rocket; +extern crate diesel; +extern crate dotenv; +extern crate r2d2; + +#[macro_use] +extern crate rocket_contrib; + +#[macro_use] +extern crate serde_derive; + +mod pg_pool; +mod schema; +use dotenv::dotenv; + #[get("/")] fn index() -> String { @@ -8,5 +22,11 @@ fn index() -> String { } fn main() { - rocket::ignite().mount("/",routes![index]).launch(); + dotenv().ok(); + let database_url = std::env::var("database_url").expect("database_url must be set"); + + rocket::ignite() + .manage(pg_pool::init(&database_url)) + .mount("/",routes![index]) + .launch(); } diff --git a/src/pg_pool.rs b/src/pg_pool.rs new file mode 100644 index 0000000..030ef43 --- /dev/null +++ b/src/pg_pool.rs @@ -0,0 +1,42 @@ +use diesel::pg::PgConnection; +use r2d2; +use diesel::r2d2::ConnectionManager; + +type ManagedPgConn = ConnectionManager; +type Pool = r2d2::Pool; + +use rocket::http::Status; +use rocket::request::{self, FromRequest}; +use rocket::{Outcome, Request, State}; +use std::ops::Deref; +/// Db Connection request guard type: wrapper around r2d2 pooled connection +pub struct DbConn(pub r2d2::PooledConnection); + +/// Attempts to retrieve a single connection from the managed database pool. If +/// no pool is currently managed, fails with an `InternalServerError` status. If +/// no connections are available, fails with a `ServiceUnavailable` status. +impl<'a, 'r> FromRequest<'a, 'r> for DbConn { + type Error = (); + + fn from_request(request: &'a Request<'r>) -> request::Outcome { + let pool = request.guard::>()?; + match pool.get() { + Ok(conn) => Outcome::Success(DbConn(conn)), + Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())), + } + } +} + +// For the convenience of using an &DbConn as an &SqliteConnection. +impl Deref for DbConn { + type Target = PgConnection; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub fn init(database_url: &str) -> Pool { + let manager = ConnectionManager::::new(database_url); + r2d2::Pool::new(manager).expect("Failed to create pool.") +} \ No newline at end of file diff --git a/src/schema.rs b/src/schema.rs new file mode 100644 index 0000000..3124ea5 --- /dev/null +++ b/src/schema.rs @@ -0,0 +1,8 @@ +table! { + posts (id) { + id -> Int4, + title -> Varchar, + body -> Text, + published -> Bool, + } +}