You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.8 KiB
197 lines
5.8 KiB
//!
|
|
|
|
// This integration relies on _knowing_ which backend is being used at compile-time
|
|
// This is a poor woman's XOR - if you know how to make it more pretty, PRs welcome <3
|
|
#[cfg(any(
|
|
all(feature = "pg", feature = "mysql"),
|
|
all(feature = "pg", feature = "sqlite3"),
|
|
all(feature = "mysql", feature = "sqlite3")
|
|
))]
|
|
compile_error!("`barrel` can only integrate with `diesel` if you select one (1) backend!");
|
|
|
|
use diesel_rs::connection::SimpleConnection;
|
|
use diesel_rs::migration::{Migration, RunMigrationsError};
|
|
use std::fs::{self, File};
|
|
use std::io::prelude::*;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
/// Represents a migration run inside Diesel
|
|
///
|
|
/// 1. Path
|
|
/// 2. Version
|
|
/// 3. Up
|
|
/// 4. Down
|
|
pub struct BarrelMigration(PathBuf, String, String, String);
|
|
|
|
impl Migration for BarrelMigration {
|
|
fn file_path(&self) -> Option<&Path> {
|
|
Some(self.0.as_path())
|
|
}
|
|
|
|
fn version(&self) -> &str {
|
|
&self.1
|
|
}
|
|
|
|
fn run(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
|
|
conn.batch_execute(&self.2)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn revert(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
|
|
conn.batch_execute(&self.3)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Generate migration files using the barrel schema builder
|
|
pub fn generate_initial(path: &PathBuf) {
|
|
generate_initial_with_content(
|
|
path,
|
|
&"fn up(migr: &mut Migration) {} \n\n".to_string(),
|
|
&"fn down(migr: &mut Migration) {} \n".to_string(),
|
|
)
|
|
}
|
|
|
|
/// Generate migration files using the barrel schema builder with initial content
|
|
pub fn generate_initial_with_content(path: &PathBuf, up_content: &String, down_content: &String) {
|
|
let migr_path = path.join("mod.rs");
|
|
println!("Creating {}", migr_path.display());
|
|
|
|
let mut barrel_migr = fs::File::create(migr_path).unwrap();
|
|
barrel_migr.write(b"/// Handle up migrations \n").unwrap();
|
|
barrel_migr.write(up_content.as_bytes()).unwrap();
|
|
|
|
barrel_migr.write(b"/// Handle down migrations \n").unwrap();
|
|
barrel_migr.write(down_content.as_bytes()).unwrap();
|
|
}
|
|
|
|
/// Generate a Migration from the provided path
|
|
pub fn migration_from(path: &Path) -> Option<Box<Migration>> {
|
|
match path.join("mod.rs").exists() {
|
|
true => Some(run_barrel_migration_wrapper(&path.join("mod.rs"))),
|
|
false => None,
|
|
}
|
|
}
|
|
|
|
fn version_from_path(path: &Path) -> Result<String, ()> {
|
|
path.parent()
|
|
.unwrap_or_else(|| {
|
|
panic!(
|
|
"Migration doesn't appear to be in a directory: `{:?}`",
|
|
path
|
|
)
|
|
})
|
|
.file_name()
|
|
.unwrap_or_else(|| panic!("Can't get file name from path `{:?}`", path))
|
|
.to_string_lossy()
|
|
.split('_')
|
|
.nth(0)
|
|
.map(|s| Ok(s.replace('-', "")))
|
|
.unwrap_or_else(|| Err(()))
|
|
}
|
|
|
|
fn run_barrel_migration_wrapper(path: &Path) -> Box<Migration> {
|
|
let (up, down) = run_barrel_migration(&path);
|
|
let version = version_from_path(path).unwrap();
|
|
let migration_path = match path.parent() {
|
|
Some(parent_path) => parent_path.to_path_buf(),
|
|
None => path.to_path_buf(),
|
|
};
|
|
Box::new(BarrelMigration(migration_path, version, up, down))
|
|
}
|
|
|
|
fn run_barrel_migration(migration: &Path) -> (String, String) {
|
|
/* Create a tmp dir with src/ child */
|
|
use tempfile::Builder;
|
|
|
|
let dir = Builder::new().prefix("barrel").tempdir().unwrap();
|
|
fs::create_dir_all(&dir.path().join("src")).unwrap();
|
|
|
|
let (feat, ident) = get_backend_pair();
|
|
|
|
let toml = format!(
|
|
"# This file is auto generated by barrel
|
|
[package]
|
|
name = \"tmp-generator\"
|
|
description = \"Doing nasty things with cargo\"
|
|
version = \"0.0.0\"
|
|
authors = [\"Katharina Fey <kookie@spacekookie.de>\"]
|
|
# TODO: Use same `barrel` dependency as crate
|
|
[dependencies]
|
|
barrel = {{ version = \"*\", features = [ {:?} ] }}",
|
|
feat
|
|
);
|
|
|
|
/* Add a Cargo.toml file */
|
|
let ct = dir.path().join("Cargo.toml");
|
|
let mut cargo_toml = File::create(&ct).unwrap();
|
|
cargo_toml.write_all(toml.as_bytes()).unwrap();
|
|
|
|
/* Generate main.rs based on user migration */
|
|
let main_file_path = &dir.path().join("src").join("main.rs");
|
|
let mut main_file = File::create(&main_file_path).unwrap();
|
|
|
|
let user_migration = migration.as_os_str().to_os_string().into_string().unwrap();
|
|
main_file
|
|
.write_all(
|
|
format!(
|
|
"//! This file is auto generated by barrel
|
|
extern crate barrel;
|
|
use barrel::*;
|
|
|
|
use barrel::backend::{ident};
|
|
|
|
include!(\"{}\");
|
|
|
|
fn main() {{
|
|
let mut m_up = Migration::new();
|
|
up(&mut m_up);
|
|
println!(\"{{}}\", m_up.make::<{ident}>());
|
|
|
|
let mut m_down = Migration::new();
|
|
down(&mut m_down);
|
|
println!(\"{{}}\", m_down.make::<{ident}>());
|
|
}}
|
|
",
|
|
user_migration,
|
|
ident = ident
|
|
)
|
|
.as_bytes(),
|
|
)
|
|
.unwrap();
|
|
|
|
let output = if cfg!(target_os = "windows") {
|
|
Command::new("cargo")
|
|
.current_dir(dir.path())
|
|
.arg("run")
|
|
.output()
|
|
.expect("failed to execute cargo!")
|
|
} else {
|
|
Command::new("sh")
|
|
.current_dir(dir.path())
|
|
.arg("-c")
|
|
.arg("cargo run")
|
|
.output()
|
|
.expect("failed to execute cargo!")
|
|
};
|
|
|
|
let output = String::from_utf8_lossy(&output.stdout);
|
|
let vec: Vec<&str> = output.split("\n").collect();
|
|
let up = String::from(vec[0]);
|
|
let down = String::from(vec[1]);
|
|
|
|
(up, down)
|
|
}
|
|
|
|
/// Uses the fact that barrel with diesel support is only compiled with _one_ feature
|
|
///
|
|
/// The first string is the feature-name, the other the struct ident
|
|
fn get_backend_pair() -> (&'static str, &'static str) {
|
|
#[cfg(feature = "pg")]
|
|
return ("pg", "Pg");
|
|
#[cfg(feature = "mysql")]
|
|
return ("mysql", "Mysql");
|
|
#[cfg(feature = "sqlite3")]
|
|
return ("sqlite3", "Sqlite");
|
|
}
|
|
|