Use anihow

This commit is contained in:
Jesper Axelsson 2020-04-10 12:56:12 +02:00 committed by Ryan Leckey
parent f00b7b48e1
commit 9a765e4e86
2 changed files with 53 additions and 43 deletions

View file

@ -21,3 +21,4 @@ sqlx = { version = "0.3", path = "..", default-features = false, features = [ "r
futures = "0.3"
structopt = "0.3"
chrono = "0.4"
anyhow = "1.0"

View file

@ -10,6 +10,8 @@ use sqlx::PgPool;
use structopt::StructOpt;
use anyhow::{Context, Result};
const MIGRATION_FOLDER: &'static str = "migrations";
/// Sqlx commandline tool
@ -41,32 +43,30 @@ enum MigrationCommand {
}
#[tokio::main]
async fn main() {
async fn main() -> Result<()> {
let opt = Opt::from_args();
match opt {
Opt::Migrate(command) => match command {
// Opt::Init { database_name } => init_migrations(&database_name),
MigrationCommand::Add { name } => add_migration_file(&name),
MigrationCommand::Run => run_migrations().await,
MigrationCommand::Add { name } => add_migration_file(&name)?,
MigrationCommand::Run => run_migrations().await?,
},
}
};
println!("All done!");
Ok(())
}
// fn init_migrations(db_name: &str) {
// println!("Initing the migrations so hard! db: {:#?}", db_name);
// }
fn add_migration_file(name: &str) {
fn add_migration_file(name: &str) -> Result<()> {
use chrono::prelude::*;
use std::path::Path;
use std::path::PathBuf;
if !Path::new(MIGRATION_FOLDER).exists() {
fs::create_dir(MIGRATION_FOLDER).expect("Failed to create 'migrations' dir")
}
fs::create_dir_all(MIGRATION_FOLDER)?;
let dt = Utc::now();
let mut file_name = dt.format("%Y-%m-%d_%H-%M-%S").to_string();
@ -78,16 +78,12 @@ fn add_migration_file(name: &str) {
path.push(MIGRATION_FOLDER);
path.push(&file_name);
if path.exists() {
eprintln!("Migration already exists!");
return;
}
let mut file = File::create(path).expect("Failed to create file");
let mut file = File::create(path).context("Failed to create file")?;
file.write_all(b"-- Add migration script here")
.expect("Could not write to file");
.context("Could not write to file")?;
println!("Created migration: '{}'", file_name);
Ok(())
}
pub struct Migration {
@ -95,8 +91,8 @@ pub struct Migration {
pub sql: String,
}
fn load_migrations() -> Vec<Migration> {
let entries = fs::read_dir(&MIGRATION_FOLDER).expect("Could not find 'migrations' dir");
fn load_migrations() -> Result<Vec<Migration>> {
let entries = fs::read_dir(&MIGRATION_FOLDER).context("Could not find 'migrations' dir")?;
let mut migrations = Vec::new();
@ -116,11 +112,11 @@ fn load_migrations() -> Vec<Migration> {
continue;
}
let mut file =
File::open(e.path()).expect(&format!("Failed to open: '{:?}'", e.file_name()));
let mut file = File::open(e.path())
.with_context(|| format!("Failed to open: '{:?}'", e.file_name()))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.expect(&format!("Failed to read: '{:?}'", e.file_name()));
.with_context(|| format!("Failed to read: '{:?}'", e.file_name()))?;
migrations.push(Migration {
name: e.file_name().to_str().unwrap().to_string(),
@ -132,42 +128,48 @@ fn load_migrations() -> Vec<Migration> {
migrations.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap());
migrations
Ok(migrations)
}
async fn run_migrations() {
// use sqlx_core::executor::Executor;
async fn run_migrations() -> Result<()> {
dotenv().ok();
let db_url = env::var("DATABASE_URL").expect("Failed to find 'DATABASE_URL'");
let db_url = env::var("DATABASE_URL").context("Failed to find 'DATABASE_URL'")?;
let mut pool = PgPool::new(&db_url)
.await
.expect("Failed to connect to pool");
.context("Failed to connect to pool")?;
create_migration_table(&mut pool).await;
create_migration_table(&mut pool).await?;
let migrations = load_migrations();
let migrations = load_migrations()?;
for mig in migrations.iter() {
let mut tx = pool.begin().await.unwrap();
let mut tx = pool.begin().await.context("Failed to start transaction")?;
if check_if_applied(&mut tx, &mig.name).await {
if check_if_applied(&mut tx, &mig.name).await? {
println!("Already applied migration: '{}'", mig.name);
continue;
}
println!("Applying migration: '{}'", mig.name);
// tx.execute(&*mig.sql).await;
sqlx::query(&mig.sql)
.execute(&mut tx)
.await
.expect(&format!("Failed to run migration {:?}", &mig.name));
.with_context(|| format!("Failed to run migration {:?}", &mig.name))?;
save_applied_migration(&mut tx, &mig.name).await;
save_applied_migration(&mut tx, &mig.name).await?;
tx.commit().await.unwrap();
tx.commit().await.context("Failed to commit changes")?;
}
Ok(())
}
async fn create_migration_table(mut pool: &PgPool) {
async fn create_migration_table(mut pool: &PgPool) -> Result<()> {
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS __migrations (
@ -178,25 +180,32 @@ CREATE TABLE IF NOT EXISTS __migrations (
)
.execute(&mut pool)
.await
.expect("Failed to create migration table");
.context("Failed to create migration table")?;
Ok(())
}
async fn check_if_applied(pool: &mut PgConnection, migration: &str) -> bool {
async fn check_if_applied(pool: &mut PgConnection, migration: &str) -> Result<bool> {
use sqlx::postgres::PgRow;
use sqlx::Row;
sqlx::query("select exists(select migration from __migrations where migration = $1) as exists")
.bind(migration.to_string())
.try_map(|row: PgRow| row.try_get("exists"))
.fetch_one(pool)
.await
.expect("Failed to check migration table")
let result = sqlx::query(
"select exists(select migration from __migrations where migration = $1) as exists",
)
.bind(migration.to_string())
.try_map(|row: PgRow| row.try_get("exists"))
.fetch_one(pool)
.await
.context("Failed to check migration table")?;
Ok(result)
}
async fn save_applied_migration(pool: &mut PgConnection, migration: &str) {
async fn save_applied_migration(pool: &mut PgConnection, migration: &str) -> Result<()> {
sqlx::query("insert into __migrations (migration) values ($1)")
.bind(migration.to_string())
.execute(pool)
.await
.expect("Failed to insert migration ");
.context("Failed to insert migration")?;
Ok(())
}