Allow strings starting with env: and dotenv: to be used

Due to implementation details of the macros 1.1 port, we will no longer
be able to take arbitrary macros as arguments to `infer_schema!` and
`infer_table_from_schema!`. We can only take literal values. In
practice, the only two useful things to take here are `env!` and
`dotenv!`. We can special case those and represent them as strings
instead.

We cannot detect whether people are attempting to use `env!` here, as it
is opaque to us by the time we reach our code in the old procedural
macros. However, the string form will be the only form that works in the
macros 1.1 implementation, and it will be deprecated along with the
syntex crate when macros 1.1 is stable.
This commit is contained in:
Sean Griffin 2016-09-08 06:45:47 -04:00
parent f4cbb2accd
commit f954369e92
8 changed files with 90 additions and 7 deletions

View File

@ -20,6 +20,13 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
* Diesel CLI can now generate bash completion. See [the readme][bash completion]
for details.
* `infer_schema!` and `infer_table_from_schema!` can now take `"env:foo"`
instead of `env!("foo")` and `"dotenv:foo"` instead of `dotenv!("foo")`. The
use of `dotenv` requires the `dotenv` feature on `diesel_codegen`, which is
included by default. Using `env!` and `dotenv!` will no longer work with
`diesel_codegen`. They continue to work with `diesel_codgen_syntex`, but that
crate will be deprecated when Macros 1.1 is in the beta channel for Rust.
[bash completion]: https://github.com/diesel-rs/diesel/blob/b1a0d9901f0f2a8c8d530ccba8173b57f332b891/diesel_cli/README.md#bash-completion
### Changed

View File

@ -10,10 +10,11 @@ repository = "https://github.com/diesel-rs/diesel/tree/master/diesel_codegen"
keywords = ["orm", "database", "postgres", "sql", "codegen"]
[dependencies]
diesel_codegen_syntex = { version = "0.7.0", default-features = false }
diesel_codegen_syntex = { path = "../diesel_codegen_syntex", default-features = false }
[features]
default = ["postgres"]
default = ["postgres", "dotenv"]
dotenv = ["diesel_codegen_syntex/dotenv"]
postgres = ["diesel_codegen_syntex/postgres"]
sqlite = ["diesel_codegen_syntex/sqlite"]

View File

@ -18,13 +18,14 @@ syntex_syntax = { version = "0.44.0", optional = true }
syntex = { version = "0.44.0", optional = true }
syntex_syntax = { version = "0.44.0", optional = true }
diesel = { version = "0.7.0", default-features = false }
dotenv = { version = "0.8.0", optional = true }
[dev-dependencies]
tempdir = "0.3.4"
dotenv = "0.8.0"
[features]
default = ["with-syntex", "postgres"]
default = ["with-syntex", "postgres", "dotenv"]
with-syntex = ["syntex", "syntex_syntax"]
postgres = ["diesel/postgres"]
sqlite = ["diesel/sqlite"]

View File

@ -17,6 +17,9 @@ extern crate syntax;
#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;
#[cfg(feature = "dotenv")]
extern crate dotenv;
#[cfg(feature = "with-syntex")]
include!(concat!(env!("OUT_DIR"), "/lib.rs"));

View File

@ -0,0 +1,54 @@
use std::borrow::Cow;
use std::env;
use std::error::Error;
pub fn extract_database_url<'a>(url: &'a str) -> Result<Cow<'a, str>, String> {
if url.starts_with("dotenv:") {
try!(load_dotenv_file());
return extract_database_url(&url[3..]);
} else if url.starts_with("env:") {
let var_name = &url[4..];
env::var(var_name)
.map(Cow::Owned)
.map_err(|e| {
format!("Failed to load environment variable {}: {}",
var_name, e.description())
})
} else {
Ok(Cow::Borrowed(url))
}
}
#[cfg(feature = "dotenv")]
fn load_dotenv_file() -> Result<(), String> {
use dotenv::dotenv;
dotenv().ok();
Ok(())
}
#[cfg(not(feature = "dotenv"))]
fn load_dotenv_file() -> Result<(), String> {
Err(String::from("The dotenv feature is required to use strings starting \
with `dotenv:`"))
}
#[test]
fn extract_database_url_returns_the_given_string() {
assert_eq!("foo", extract_database_url("foo").unwrap());
assert_eq!("bar", extract_database_url("bar").unwrap());
}
#[test]
fn extract_database_url_returns_env_vars() {
env::set_var("foo", "lololol");
env::set_var("bar", "trolololol");
assert_eq!("lololol", extract_database_url("env:foo").unwrap());
assert_eq!("trolololol", extract_database_url("env:bar").unwrap());
}
#[test]
fn extract_database_url_errors_if_env_var_is_unset() {
env::remove_var("foo");
assert!(extract_database_url("env:foo").is_err());
}

View File

@ -1,4 +1,5 @@
mod data_structures;
mod database_url;
#[cfg(feature = "postgres")]
mod pg;
#[cfg(feature = "sqlite")]
@ -14,6 +15,7 @@ use syntax::util::small_vector::SmallVector;
use syntax::tokenstream::TokenTree;
use self::data_structures::*;
use self::database_url::extract_database_url;
use util::comma_delimited_tokens;
pub fn expand_load_table<'cx>(
@ -41,7 +43,7 @@ pub fn load_table_body<T: Iterator<Item=P<ast::Expr>>>(
sp: Span,
exprs: &mut T,
) -> Result<Box<MacResult>, Box<MacResult>> {
let database_url = try!(next_str_lit(cx, sp, exprs));
let database_url = try!(database_url(cx, sp, exprs));
let table_name = try!(next_str_lit(cx, sp, exprs));
let connection = try!(establish_connection(cx, sp, &database_url));
table_macro_call(cx, sp, &connection, &table_name)
@ -69,7 +71,7 @@ pub fn infer_schema_body<T: Iterator<Item=P<ast::Expr>>>(
sp: Span,
exprs: &mut T,
) -> Result<Box<MacResult>, Box<MacResult>> {
let database_url = try!(next_str_lit(cx, sp, exprs));
let database_url = try!(database_url(cx, sp, exprs));
let connection = try!(establish_connection(cx, sp, &database_url));
let table_names = load_table_names(&connection).unwrap();
let impls = table_names.into_iter()
@ -226,3 +228,18 @@ fn get_primary_keys(conn: &InferConnection, table_name: &str) -> QueryResult<Vec
InferConnection::Pg(ref c) => pg::get_primary_keys(c, table_name),
}
}
fn database_url<T: Iterator<Item=P<ast::Expr>>>(
cx: &mut ExtCtxt,
sp: Span,
exprs: &mut T,
) -> Result<String, Box<MacResult>> {
let database_url = try!(next_str_lit(cx, sp, exprs));
match extract_database_url(&database_url) {
Ok(s) => Ok(s.into_owned()),
Err(msg) => {
cx.span_err(sp, &msg);
Err(DummyResult::any(sp))
}
}
}

View File

@ -17,7 +17,7 @@ assert_matches = "1.0.1"
chrono = { version = "^0.2.17" }
diesel = { path = "../diesel", default-features = false, features = ["quickcheck", "chrono", "uuid"] }
diesel_codegen = { version = "0.7.2", optional = true }
diesel_codegen_old = { path = "../diesel_codegen_old", default-features = false, optional = true }
diesel_codegen_old = { path = "../diesel_codegen_old", default-features = false, features = ["dotenv"], optional = true }
dotenv_macros = { version = "0.9.0", optional = true }
quickcheck = { version = "0.3.1", features = ["unstable"] }
uuid = { version = ">=0.2.0, <0.4.0" }

View File

@ -1,6 +1,6 @@
use diesel::*;
infer_schema!(dotenv!("DATABASE_URL"));
infer_schema!("dotenv:DATABASE_URL");
#[derive(PartialEq, Eq, Debug, Clone, Queryable, Identifiable, Insertable, AsChangeset, Associations)]
#[has_many(posts)]