Skip to content

Commit 38dc91b

Browse files
authored
Merge pull request diesel-rs#2399 from weiznich/embed_migrations_as_function_like_macro
Rewrite `embed_migrations!` to be a real proc macro
2 parents 4aa07e9 + 9adaa4b commit 38dc91b

8 files changed

Lines changed: 113 additions & 144 deletions

File tree

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ script:
7171
(cd "examples/$BACKEND" && ./test_all) &&
7272
(cd diesel_cli && cargo test --no-default-features --features "$BACKEND") &&
7373
(cd diesel_migrations/migrations_internals && cargo test ) &&
74-
(cd diesel_migrations/migrations_macros && cargo test ) &&
74+
(cd diesel_migrations/migrations_macros && cargo test --features "$BACKEND diesel/$BACKEND" ) &&
7575
(cd diesel_migrations/ && cargo test --features "$BACKEND diesel/$BACKEND" ) &&
7676
if [[ "$TRAVIS_RUST_VERSION" == nightly* ]]; then
7777
(cd diesel_tests && cargo test --no-default-features --features "unstable $BACKEND")

_build/azure-pipelines-template.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
rust_version: $(RUSTUP_TOOLCHAIN)
6161
bash: |
6262
(cd diesel_migrations/migrations_internals && cargo test ) &&
63-
(cd diesel_migrations/migrations_macros && cargo test ) &&
63+
(cd diesel_migrations/migrations_macros && cargo test --features "$BACKEND diesel/$BACKEND" ) &&
6464
(cd diesel_migrations/ && cargo test --features "$BACKEND diesel/$BACKEND" )
6565
displayName: Test diesel-migrations
6666
- template: failable_step.yml

diesel/src/doctest_setup.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
extern crate dotenv;
2-
#[macro_use] extern crate cfg_if;
3-
41
use diesel::prelude::*;
5-
use self::dotenv::dotenv;
2+
use dotenv::dotenv;
63

7-
cfg_if! {
4+
cfg_if::cfg_if! {
85
if #[cfg(feature = "postgres")] {
96
#[allow(dead_code)]
107
type DB = diesel::pg::Pg;
@@ -104,7 +101,7 @@ cfg_if! {
104101
(1, 'My first post'),
105102
(1, 'About Rust'),
106103
(2, 'My first post too')").unwrap();
107-
104+
108105
connection.execute("CREATE TABLE comments (
109106
id INTEGER PRIMARY KEY AUTOINCREMENT,
110107
post_id INTEGER NOT NULL,
@@ -161,7 +158,7 @@ cfg_if! {
161158
(1, 'My first post'),
162159
(1, 'About Rust'),
163160
(2, 'My first post too')").unwrap();
164-
161+
165162
connection.execute("CREATE TABLE comments (
166163
id INTEGER PRIMARY KEY AUTO_INCREMENT,
167164
post_id INTEGER NOT NULL,
@@ -195,7 +192,6 @@ fn database_url_from_env(backend_specific_env_var: &str) -> String {
195192
.expect("DATABASE_URL must be set in order to run tests")
196193
}
197194

198-
199195
mod schema {
200196
use diesel::prelude::*;
201197

diesel_migrations/migrations_macros/Cargo.toml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0"
66
description = "Codegeneration macros for diesels embedded migrations"
77
documentation = "https://docs.diesel.rs"
88
homepage = "https://diesel.rs"
9+
edition = "2018"
910

1011
[dependencies]
11-
syn = { version = "1", features = ["extra-traits"] }
1212
quote = "1"
1313
proc-macro2 = "1"
1414

@@ -18,9 +18,24 @@ path = "../migrations_internals"
1818

1919
[dev-dependencies]
2020
tempfile = "3.0.0"
21+
dotenv = ">=0.8, <0.11"
22+
cfg-if = "0.1.0"
23+
24+
[dev-dependencies.diesel]
25+
version = "~2.0.0"
26+
path = "../../diesel"
27+
default-features = false
28+
29+
[dev-dependencies.diesel_migrations]
30+
version = "~1.4.0"
31+
path = "../"
32+
default-features = false
2133

2234
[lib]
2335
proc-macro = true
2436

2537
[features]
2638
default = []
39+
sqlite = []
40+
postgres = []
41+
mysql = []

diesel_migrations/migrations_macros/src/embed_migrations.rs

Lines changed: 42 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
1-
use proc_macro2;
2-
use syn;
3-
4-
use migrations::migration_directory_from_given_path;
1+
use crate::migrations::migration_directory_from_given_path;
52
use migrations_internals::{migration_paths_in_directory, version_from_path};
3+
use quote::quote;
64
use std::error::Error;
75
use std::fs::DirEntry;
86
use std::path::Path;
97

10-
use util::{get_option, get_options_from_input};
11-
12-
pub fn derive_embed_migrations(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
13-
fn bug() -> ! {
14-
panic!(
15-
"This is a bug. Please open a Github issue \
16-
with your invocation of `embed_migrations!"
17-
);
18-
}
19-
20-
let options =
21-
get_options_from_input(&parse_quote!(embed_migrations_options), &input.attrs, bug);
22-
let migrations_path_opt = options
23-
.as_ref()
24-
.map(|o| get_option(o, "migrations_path", bug));
8+
pub fn expand(path: String) -> proc_macro2::TokenStream {
9+
dbg!(&path);
10+
let migrations_path_opt = if path.is_empty() {
11+
None
12+
} else {
13+
Some(path.replace("\"", ""))
14+
};
2515
let migrations_expr =
2616
migration_directory_from_given_path(migrations_path_opt.as_ref().map(String::as_str))
2717
.and_then(|path| migration_literals_from_path(&path));
@@ -30,54 +20,48 @@ pub fn derive_embed_migrations(input: &syn::DeriveInput) -> proc_macro2::TokenSt
3020
Err(e) => panic!("Error reading migrations: {}", e),
3121
};
3222

33-
// These are split into multiple `quote!` calls to avoid recursion limit
34-
let embedded_migration_def = quote!(
35-
struct EmbeddedMigration {
36-
version: &'static str,
37-
up_sql: &'static str,
38-
}
23+
quote! {
24+
#[allow(dead_code)]
25+
mod embedded_migrations {
26+
extern crate diesel;
27+
extern crate diesel_migrations;
3928

40-
impl Migration for EmbeddedMigration {
41-
fn version(&self) -> &str {
42-
self.version
43-
}
29+
use self::diesel_migrations::*;
30+
use self::diesel::connection::SimpleConnection;
31+
use std::io;
4432

45-
fn run(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
46-
conn.batch_execute(self.up_sql).map_err(Into::into)
47-
}
33+
const ALL_MIGRATIONS: &[&Migration] = &[#(#migrations_expr),*];
4834

49-
fn revert(&self, _conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
50-
unreachable!()
35+
struct EmbeddedMigration {
36+
version: &'static str,
37+
up_sql: &'static str,
5138
}
52-
}
53-
);
5439

55-
let run_fns = quote!(
56-
pub fn run<C: MigrationConnection>(conn: &C) -> Result<(), RunMigrationsError> {
57-
run_with_output(conn, &mut io::sink())
58-
}
59-
60-
pub fn run_with_output<C: MigrationConnection>(
61-
conn: &C,
62-
out: &mut io::Write,
63-
) -> Result<(), RunMigrationsError> {
64-
run_migrations(conn, ALL_MIGRATIONS.iter().map(|v| *v), out)
65-
}
66-
);
67-
68-
quote! {
69-
extern crate diesel;
70-
extern crate diesel_migrations;
40+
impl Migration for EmbeddedMigration {
41+
fn version(&self) -> &str {
42+
self.version
43+
}
7144

72-
use self::diesel_migrations::*;
73-
use self::diesel::connection::SimpleConnection;
74-
use std::io;
45+
fn run(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
46+
conn.batch_execute(self.up_sql).map_err(Into::into)
47+
}
7548

76-
const ALL_MIGRATIONS: &[&Migration] = &[#(#migrations_expr),*];
49+
fn revert(&self, _conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
50+
unreachable!()
51+
}
52+
}
7753

78-
#embedded_migration_def
54+
pub fn run<C: MigrationConnection>(conn: &C) -> Result<(), RunMigrationsError> {
55+
run_with_output(conn, &mut io::sink())
56+
}
7957

80-
#run_fns
58+
pub fn run_with_output<C: MigrationConnection>(
59+
conn: &C,
60+
out: &mut io::Write,
61+
) -> Result<(), RunMigrationsError> {
62+
run_migrations(conn, ALL_MIGRATIONS.iter().map(|v| *v), out)
63+
}
64+
}
8165
}
8266
}
8367

diesel_migrations/migrations_macros/src/lib.rs

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
clippy::option_map_unwrap_or_else,
66
clippy::option_map_unwrap_or,
77
clippy::match_same_arms,
8-
clippy::type_complexity
8+
clippy::type_complexity,
9+
clippy::needless_doctest_main
910
)]
1011
#![warn(
1112
clippy::option_unwrap_used,
@@ -21,25 +22,57 @@
2122
clippy::used_underscore_binding
2223
)]
2324
#![cfg_attr(test, allow(clippy::option_unwrap_used, clippy::result_unwrap_used))]
24-
extern crate migrations_internals;
2525
extern crate proc_macro;
26-
extern crate proc_macro2;
27-
#[macro_use]
28-
extern crate quote;
29-
#[macro_use]
30-
extern crate syn;
3126

3227
mod embed_migrations;
3328
mod migrations;
34-
mod util;
3529

3630
use proc_macro::TokenStream;
37-
use syn::DeriveInput;
3831

39-
#[proc_macro_derive(EmbedMigrations, attributes(embed_migrations_options))]
40-
pub fn derive_embed_migrations(input: TokenStream) -> TokenStream {
41-
let item = parse_macro_input!(input as DeriveInput);
42-
embed_migrations::derive_embed_migrations(&item)
32+
/// This macro will read your migrations at compile time, and embed a module you can use to execute
33+
/// them at runtime without the migration files being present on the file system. This is useful if
34+
/// you would like to use Diesel's migration infrastructure, but want to ship a single executable
35+
/// file (such as for embedded applications). It can also be used to apply migrations to an in
36+
/// memory database (Diesel does this for its own test suite).
37+
///
38+
/// You can optionally pass the path to the migrations directory to this macro. When left
39+
/// unspecified, Diesel Codegen will search for the migrations directory in the same way that
40+
/// Diesel CLI does. If specified, the path should be relative to the directory where `Cargo.toml`
41+
/// resides.
42+
///
43+
/// # Examples
44+
///
45+
/// ```rust
46+
/// # use diesel_migrations::embed_migrations;
47+
/// # include!("../../../diesel/src/doctest_setup.rs");
48+
/// # table! {
49+
/// # users {
50+
/// # id -> Integer,
51+
/// # name -> VarChar,
52+
/// # }
53+
/// # }
54+
/// #
55+
/// # #[cfg(feature = "postgres")]
56+
/// # embed_migrations!("../../migrations/postgresql");
57+
/// # #[cfg(all(feature = "mysql", not(feature = "postgres")))]
58+
/// # embed_migrations!("../../migrations/mysql");
59+
/// # #[cfg(all(feature = "sqlite", not(any(feature = "postgres", feature = "mysql"))))]
60+
/// embed_migrations!("../../migrations/sqlite");
61+
///
62+
/// fn main() {
63+
/// let connection = establish_connection();
64+
///
65+
/// // This will run the necessary migrations.
66+
/// embedded_migrations::run(&connection);
67+
///
68+
/// // By default the output is thrown out. If you want to redirect it to stdout, you
69+
/// // should call embedded_migrations::run_with_output.
70+
/// embedded_migrations::run_with_output(&connection, &mut std::io::stdout());
71+
/// }
72+
/// ```
73+
#[proc_macro]
74+
pub fn embed_migrations(input: TokenStream) -> TokenStream {
75+
embed_migrations::expand(input.to_string())
4376
.to_string()
4477
.parse()
4578
.unwrap()

diesel_migrations/migrations_macros/src/migrations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fn resolve_migrations_directory(
3232

3333
#[cfg(test)]
3434
mod tests {
35-
extern crate tempfile;
35+
use tempfile;
3636

3737
use self::tempfile::Builder;
3838
use super::resolve_migrations_directory;

diesel_migrations/src/lib.rs

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -130,64 +130,5 @@ pub mod connection {
130130
pub use migrations_internals::connection::MigrationConnection;
131131
}
132132

133-
#[macro_export]
134-
/// This macro will read your migrations at compile time, and embed a module you can use to execute
135-
/// them at runtime without the migration files being present on the file system. This is useful if
136-
/// you would like to use Diesel's migration infrastructure, but want to ship a single executable
137-
/// file (such as for embedded applications). It can also be used to apply migrations to an in
138-
/// memory database (Diesel does this for its own test suite).
139-
///
140-
/// You can optionally pass the path to the migrations directory to this macro. When left
141-
/// unspecified, Diesel Codegen will search for the migrations directory in the same way that
142-
/// Diesel CLI does. If specified, the path should be relative to the directory where `Cargo.toml`
143-
/// resides.
144-
///
145-
/// # Examples
146-
///
147-
/// ```rust
148-
/// # #[macro_use] extern crate diesel;
149-
/// # #[macro_use] extern crate diesel_migrations;
150-
/// # include!("../../diesel/src/doctest_setup.rs");
151-
/// # table! {
152-
/// # users {
153-
/// # id -> Integer,
154-
/// # name -> VarChar,
155-
/// # }
156-
/// # }
157-
/// #
158-
/// # #[cfg(feature = "postgres")]
159-
/// # embed_migrations!("../migrations/postgresql");
160-
/// # #[cfg(all(feature = "mysql", not(feature = "postgres")))]
161-
/// # embed_migrations!("../migrations/mysql");
162-
/// # #[cfg(all(feature = "sqlite", not(any(feature = "postgres", feature = "mysql"))))]
163-
/// embed_migrations!("../migrations/sqlite");
164-
///
165-
/// fn main() {
166-
/// let connection = establish_connection();
167-
///
168-
/// // This will run the necessary migrations.
169-
/// embedded_migrations::run(&connection);
170-
///
171-
/// // By default the output is thrown out. If you want to redirect it to stdout, you
172-
/// // should call embedded_migrations::run_with_output.
173-
/// embedded_migrations::run_with_output(&connection, &mut std::io::stdout());
174-
/// }
175-
/// ```
176-
macro_rules! embed_migrations {
177-
() => {
178-
#[allow(dead_code)]
179-
mod embedded_migrations {
180-
#[derive(EmbedMigrations)]
181-
struct _Dummy;
182-
}
183-
};
184-
185-
($migrations_path:expr) => {
186-
#[allow(dead_code)]
187-
mod embedded_migrations {
188-
#[derive(EmbedMigrations)]
189-
#[embed_migrations_options(migrations_path=$migrations_path)]
190-
struct _Dummy;
191-
}
192-
};
193-
}
133+
#[doc(inline)]
134+
pub use migrations_macros::embed_migrations;

0 commit comments

Comments
 (0)