mirror of
https://github.com/launchbadge/sqlx.git
synced 2026-01-07 01:01:39 +00:00
Add migrate! macro for embedded migrations
This commit is contained in:
parent
60c3ece671
commit
e5e9665bd9
@ -39,7 +39,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
[features]
|
||||
default = [ "macros", "runtime-async-std", "migrate" ]
|
||||
macros = [ "sqlx-macros" ]
|
||||
migrate = [ "sqlx-core/migrate" ]
|
||||
migrate = [ "sqlx-macros/migrate", "sqlx-core/migrate" ]
|
||||
|
||||
# [deprecated] TLS is not possible to disable due to it being conditional on multiple features
|
||||
# Hopefully Cargo can handle this in the future
|
||||
|
||||
@ -50,7 +50,7 @@ pub async fn info(uri: &str) -> anyhow::Result<()> {
|
||||
} else {
|
||||
style("pending").yellow()
|
||||
},
|
||||
migration.description(),
|
||||
migration.description,
|
||||
);
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ pub async fn run(uri: &str) -> anyhow::Result<()> {
|
||||
"{}/{} {} {}",
|
||||
style(migration.version()).cyan(),
|
||||
style("migrate").green(),
|
||||
migration.description(),
|
||||
migration.description,
|
||||
style(format!("({:?})", elapsed)).dim()
|
||||
);
|
||||
} else {
|
||||
|
||||
@ -71,7 +71,7 @@ impl Migrator {
|
||||
}
|
||||
|
||||
for migration in self.iter() {
|
||||
if migration.version() > version {
|
||||
if migration.version > version {
|
||||
conn.apply(migration).await?;
|
||||
} else {
|
||||
conn.validate(migration).await?;
|
||||
|
||||
@ -16,7 +16,8 @@ authors = [
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
default = [ "runtime-async-std" ]
|
||||
default = [ "runtime-async-std", "migrate" ]
|
||||
migrate = [ "sha2" ]
|
||||
|
||||
# runtimes
|
||||
runtime-async-std = [ "sqlx-core/runtime-async-std", "sqlx-rt/runtime-async-std" ]
|
||||
|
||||
@ -16,6 +16,9 @@ mod database;
|
||||
mod derives;
|
||||
mod query;
|
||||
|
||||
#[cfg(feature = "migrate")]
|
||||
mod migrate;
|
||||
|
||||
fn macro_result(tokens: proc_macro2::TokenStream) -> TokenStream {
|
||||
quote!(
|
||||
macro_rules! macro_result {
|
||||
@ -79,6 +82,29 @@ pub fn derive_from_row(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "migrate")]
|
||||
#[proc_macro]
|
||||
pub fn migrate(input: TokenStream) -> TokenStream {
|
||||
use syn::LitStr;
|
||||
|
||||
let input = syn::parse_macro_input!(input as Option<LitStr>);
|
||||
let dir = input
|
||||
.as_ref()
|
||||
.map_or("migrations".to_owned(), LitStr::value);
|
||||
|
||||
match migrate::expand_migrator_from_dir(dir) {
|
||||
Ok(ts) => ts.into(),
|
||||
Err(e) => {
|
||||
if let Some(parse_err) = e.downcast_ref::<syn::Error>() {
|
||||
macro_result(parse_err.to_compile_error())
|
||||
} else {
|
||||
let msg = e.to_string();
|
||||
macro_result(quote!(compile_error!(#msg)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[proc_macro_attribute]
|
||||
pub fn test(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
|
||||
91
sqlx-macros/src/migrate.rs
Normal file
91
sqlx-macros/src/migrate.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use sha2::{Digest, Sha384};
|
||||
use std::fs;
|
||||
use std::fs::DirEntry;
|
||||
use std::path::Path;
|
||||
|
||||
struct QuotedMigration {
|
||||
version: i64,
|
||||
description: String,
|
||||
sql: String,
|
||||
checksum: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ToTokens for QuotedMigration {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let QuotedMigration {
|
||||
version,
|
||||
description,
|
||||
sql,
|
||||
checksum,
|
||||
} = &self;
|
||||
|
||||
let ts = quote! {
|
||||
sqlx::migrate::Migration {
|
||||
version: #version,
|
||||
description: std::borrow::Cow::Borrowed(#description),
|
||||
sql: std::borrow::Cow::Borrowed(#sql),
|
||||
checksum: std::borow::Cow::Borrowed(&[
|
||||
#(#checksum),*
|
||||
]),
|
||||
}
|
||||
};
|
||||
|
||||
tokens.append_all(ts.into_iter());
|
||||
}
|
||||
}
|
||||
|
||||
// mostly copied from sqlx-core/src/migrate/source.rs
|
||||
pub(crate) fn expand_migrator_from_dir<P: AsRef<Path>>(
|
||||
dir: P,
|
||||
) -> crate::Result<proc_macro2::TokenStream> {
|
||||
let mut s = fs::read_dir(dir.as_ref().canonicalize()?)?;
|
||||
let mut migrations = Vec::new();
|
||||
|
||||
while let Some(entry) = s.next() {
|
||||
let entry = entry?;
|
||||
if !entry.metadata()?.is_file() {
|
||||
// not a file; ignore
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_name = entry.file_name();
|
||||
let file_name = file_name.to_string_lossy();
|
||||
|
||||
let parts = file_name.splitn(2, '_').collect::<Vec<_>>();
|
||||
|
||||
if parts.len() != 2 || !parts[1].ends_with(".sql") {
|
||||
// not of the format: <VERSION>_<DESCRIPTION>.sql; ignore
|
||||
continue;
|
||||
}
|
||||
|
||||
let version: i64 = parts[0].parse()?;
|
||||
|
||||
// remove the `.sql` and replace `_` with ` `
|
||||
let description = parts[1]
|
||||
.trim_end_matches(".sql")
|
||||
.replace('_', " ")
|
||||
.to_owned();
|
||||
|
||||
let sql = fs::read_to_string(&entry.path())?;
|
||||
|
||||
let checksum = Vec::from(Sha384::digest(sql.as_bytes()).as_slice());
|
||||
|
||||
migrations.push(QuotedMigration {
|
||||
version,
|
||||
description,
|
||||
sql,
|
||||
checksum,
|
||||
})
|
||||
}
|
||||
|
||||
// ensure that we are sorted by `VERSION ASC`
|
||||
migrations.sort_by_key(|m| m.version);
|
||||
|
||||
Ok(quote! {
|
||||
sqlx::migrate::Migrator::from_static(&[
|
||||
#(#migrations),*
|
||||
])
|
||||
})
|
||||
}
|
||||
@ -61,6 +61,10 @@ pub extern crate sqlx_macros;
|
||||
#[doc(hidden)]
|
||||
pub use sqlx_macros::{FromRow, Type};
|
||||
|
||||
// embedded migrations
|
||||
#[cfg(all(feature = "migrate", features = "macros"))]
|
||||
pub use sqlx_macros::migrate;
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod macros;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user