Skip to content

Commit e772508

Browse files
committed
Give diesel.toml the same capabilities as every config flag
This gives every existing flag that can be given to `diesel print-schema` an equivalent entry in `diesel.toml`, with the exception of `database-url` which should never be in this file. There are more manual serde impls than I would have liked here. Particularly, the impl for `Filtering` should have just been derived, but the derived impl just blows up with "expected string for key `fields`" -- I have no clue why, and it's really opaque what's going on.
1 parent ae22270 commit e772508

17 files changed

Lines changed: 202 additions & 44 deletions

File tree

diesel_cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ clap = "2.27"
2020
clippy = { optional = true, version = "=0.0.185" }
2121
diesel = { version = "~1.2.0", default-features = false }
2222
dotenv = ">=0.8, <0.11"
23-
infer_schema_internals = "~1.2.0"
23+
infer_schema_internals = { version = "~1.2.0", features = ["serde"] }
2424
migrations_internals = "~1.2.0"
2525
serde = { version = "1.0.0", features = ["derive"] }
2626
toml = "0.4.6"

diesel_cli/src/config.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ use std::path::PathBuf;
77
use toml;
88

99
use super::{find_project_root, handle_error};
10+
use print_schema;
1011

1112
#[derive(Deserialize)]
13+
#[serde(deny_unknown_fields)]
1214
pub struct Config {
1315
#[serde(default)]
1416
pub print_schema: PrintSchema,
@@ -36,6 +38,20 @@ impl Config {
3638
}
3739

3840
#[derive(Default, Deserialize)]
41+
#[serde(deny_unknown_fields)]
3942
pub struct PrintSchema {
43+
#[serde(default)]
4044
pub file: Option<PathBuf>,
45+
#[serde(default)]
46+
pub with_docs: bool,
47+
#[serde(default)]
48+
pub filter: print_schema::Filtering,
49+
#[serde(default)]
50+
pub schema: Option<String>,
51+
}
52+
53+
impl PrintSchema {
54+
pub fn schema_name(&self) -> Option<&str> {
55+
self.schema.as_ref().map(|s| &**s)
56+
}
4157
}

diesel_cli/src/main.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn main() {
5656
("setup", Some(matches)) => run_setup_command(matches),
5757
("database", Some(matches)) => run_database_command(matches),
5858
("bash-completion", Some(matches)) => generate_bash_completion_command(matches),
59-
("print-schema", Some(matches)) => run_infer_schema(matches),
59+
("print-schema", Some(matches)) => run_infer_schema(matches).unwrap_or_else(handle_error),
6060
_ => unreachable!("The cli parser should prevent reaching here"),
6161
}
6262
}
@@ -313,53 +313,53 @@ fn convert_absolute_path_to_relative(target_path: &Path, mut current_path: &Path
313313
result.join(target_path.strip_prefix(current_path).unwrap())
314314
}
315315

316-
fn run_infer_schema(matches: &ArgMatches) {
316+
fn run_infer_schema(matches: &ArgMatches) -> Result<(), Box<Error>> {
317317
use infer_schema_internals::TableName;
318318
use print_schema::*;
319319

320320
let database_url = database::database_url(matches);
321-
let schema_name = matches.value_of("schema");
321+
let mut config = Config::read(matches)?.print_schema;
322+
323+
if let Some(schema_name) = matches.value_of("schema") {
324+
config.schema = Some(String::from(schema_name))
325+
}
322326

323327
let filter = matches
324328
.values_of("table-name")
325329
.unwrap_or_default()
326330
.map(|table_name| {
327-
if let Some(schema) = schema_name {
331+
if let Some(schema) = config.schema_name() {
328332
TableName::new(table_name, schema)
329333
} else {
330334
table_name.parse().unwrap()
331335
}
332336
})
333337
.collect();
334338

335-
let filter = if matches.is_present("whitelist") {
336-
Filtering::Whitelist(filter)
339+
if matches.is_present("whitelist") {
340+
config.filter = Filtering::Whitelist(filter)
337341
} else if matches.is_present("blacklist") {
338-
Filtering::Blacklist(filter)
339-
} else {
340-
Filtering::None
341-
};
342+
config.filter = Filtering::Blacklist(filter)
343+
}
342344

343-
let _ = run_print_schema(
344-
&database_url,
345-
schema_name,
346-
&filter,
347-
matches.is_present("with-docs"),
348-
).map_err(handle_error::<_, ()>);
345+
if matches.is_present("with-docs") {
346+
config.with_docs = true;
347+
}
348+
349+
run_print_schema(&database_url, &config)?;
350+
Ok(())
349351
}
350352

351353
fn regenerate_schema_if_file_specified(matches: &ArgMatches) -> Result<(), Box<Error>> {
352-
use print_schema::*;
353-
354354
let config = Config::read(matches)?;
355-
if let Some(path) = config.print_schema.file {
355+
if let Some(ref path) = config.print_schema.file {
356356
if let Some(parent) = path.parent() {
357357
fs::create_dir_all(parent)?;
358358
}
359359

360360
let database_url = database::database_url(matches);
361361
let mut file = fs::File::create(path)?;
362-
print_schema::output_schema(&database_url, None, &Filtering::None, false, &mut file)?;
362+
print_schema::output_schema(&database_url, &config.print_schema, &mut file)?;
363363
}
364364
Ok(())
365365
}

diesel_cli/src/print_schema.rs

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
use config;
2+
13
use infer_schema_internals::*;
24
use std::error::Error;
35
use std::fmt::{self, Display, Formatter, Write};
46
use std::io::{self, stdout};
7+
use serde::de::{self, MapAccess, Visitor};
8+
use serde::{Deserialize, Deserializer};
59

610
pub enum Filtering {
711
Whitelist(Vec<TableName>),
812
Blacklist(Vec<TableName>),
913
None,
1014
}
1115

16+
impl Default for Filtering {
17+
fn default() -> Self {
18+
Filtering::None
19+
}
20+
}
21+
1222
impl Filtering {
1323
pub fn should_ignore_table(&self, name: &TableName) -> bool {
1424
use self::Filtering::*;
@@ -23,31 +33,21 @@ impl Filtering {
2333

2434
pub fn run_print_schema(
2535
database_url: &str,
26-
schema_name: Option<&str>,
27-
filtering: &Filtering,
28-
include_docs: bool,
36+
config: &config::PrintSchema,
2937
) -> Result<(), Box<Error>> {
30-
output_schema(
31-
database_url,
32-
schema_name,
33-
filtering,
34-
include_docs,
35-
&mut stdout(),
36-
)
38+
output_schema(database_url, config, &mut stdout())
3739
}
3840

3941
pub fn output_schema<W: io::Write>(
4042
database_url: &str,
41-
schema_name: Option<&str>,
42-
filtering: &Filtering,
43-
include_docs: bool,
43+
config: &config::PrintSchema,
4444
out: &mut W,
4545
) -> Result<(), Box<Error>> {
46-
let table_names = load_table_names(database_url, schema_name)?
46+
let table_names = load_table_names(database_url, config.schema_name())?
4747
.into_iter()
48-
.filter(|t| !filtering.should_ignore_table(t))
48+
.filter(|t| !config.filter.should_ignore_table(t))
4949
.collect::<Vec<_>>();
50-
let foreign_keys = load_foreign_key_constraints(database_url, schema_name)?;
50+
let foreign_keys = load_foreign_key_constraints(database_url, config.schema_name())?;
5151
let foreign_keys =
5252
remove_unsafe_foreign_keys_for_codegen(database_url, &foreign_keys, &table_names);
5353
let table_data = table_names
@@ -57,10 +57,10 @@ pub fn output_schema<W: io::Write>(
5757
let definitions = TableDefinitions {
5858
tables: table_data,
5959
fk_constraints: foreign_keys,
60-
include_docs,
60+
include_docs: config.with_docs,
6161
};
6262

63-
if let Some(schema_name) = schema_name {
63+
if let Some(schema_name) = config.schema_name() {
6464
write!(out, "{}", ModuleDefinition(schema_name, definitions))?;
6565
} else {
6666
write!(out, "{}", definitions)?;
@@ -255,3 +255,53 @@ impl<'a, 'b: 'a> Write for PadAdapter<'a, 'b> {
255255
Ok(())
256256
}
257257
}
258+
259+
impl<'de> Deserialize<'de> for Filtering {
260+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
261+
where
262+
D: Deserializer<'de>,
263+
{
264+
struct FilteringVisitor;
265+
266+
impl<'de> Visitor<'de> for FilteringVisitor {
267+
type Value = Filtering;
268+
269+
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
270+
f.write_str("either a whitelist or a blacklist")
271+
}
272+
273+
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
274+
where
275+
V: MapAccess<'de>,
276+
{
277+
let mut whitelist = None;
278+
let mut blacklist = None;
279+
while let Some((key, value)) = map.next_entry()? {
280+
match key {
281+
"whitelist" => {
282+
if whitelist.is_some() {
283+
return Err(de::Error::duplicate_field("whitelist"));
284+
}
285+
whitelist = Some(value);
286+
}
287+
"blacklist" => {
288+
if blacklist.is_some() {
289+
return Err(de::Error::duplicate_field("blacklist"));
290+
}
291+
blacklist = Some(value);
292+
}
293+
_ => return Err(de::Error::unknown_field(key, &["whitelist", "blacklist"])),
294+
}
295+
}
296+
match (whitelist, blacklist) {
297+
(Some(_), Some(_)) => Err(de::Error::duplicate_field("blacklist")),
298+
(Some(w), None) => Ok(Filtering::Whitelist(w)),
299+
(None, Some(b)) => Ok(Filtering::Blacklist(b)),
300+
(None, None) => Ok(Filtering::None),
301+
}
302+
}
303+
}
304+
305+
deserializer.deserialize_map(FilteringVisitor)
306+
}
307+
}

diesel_cli/tests/print_schema.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,26 +80,48 @@ fn test_print_schema(test_name: &str, args: Vec<&str>) {
8080
let test_path = Path::new(env!("CARGO_MANIFEST_DIR"))
8181
.join("tests")
8282
.join("print_schema")
83-
.join(test_name)
84-
.join(BACKEND);
83+
.join(test_name);
84+
let backend_path = test_path.join(BACKEND);
8585
let p = project(test_name).build();
8686
let db = database(&p.database_url());
8787

8888
p.command("setup").run();
8989

90-
let schema = read_file(&test_path.join("schema.sql"));
90+
let schema = read_file(&backend_path.join("schema.sql"));
9191
db.execute(&schema);
9292

9393
let result = p.command("print-schema").args(args).run();
9494

9595
assert!(result.is_success(), "Result was unsuccessful {:?}", result);
96-
let expected = read_file(&test_path.join("expected.rs"));
96+
let expected = read_file(&backend_path.join("expected.rs"));
97+
98+
assert_diff!(&expected, result.stdout(), "\n", 0);
99+
100+
test_print_schema_config(test_name, &test_path, schema, expected);
101+
}
102+
103+
fn test_print_schema_config(test_name: &str, test_path: &Path, schema: String, expected: String) {
104+
let config = read_file(&test_path.join("diesel.toml"));
105+
let p = project(&format!("{}_config", test_name))
106+
.file("diesel.toml", &config)
107+
.build();
108+
109+
p.command("setup").run();
110+
p.create_migration("12345_create_schema", &schema, "");
111+
112+
let result = p.command("migration").arg("run").run();
113+
assert!(result.is_success(), "Result was unsuccessful {:?}", result);
114+
115+
let schema = p.file_contents("src/schema.rs");
116+
assert_diff!(&expected, &schema, "\n", 0);
117+
118+
let result = p.command("print-schema").run();
119+
assert!(result.is_success(), "Result was unsuccessful {:?}", result);
97120

98121
assert_diff!(&expected, result.stdout(), "\n", 0);
99122
}
100123

101124
fn read_file(path: &Path) -> String {
102-
println!("{}", path.display());
103125
let mut file = File::open(path).expect(&format!("Could not open {}", path.display()));
104126
let mut string = String::new();
105127
file.read_to_string(&mut string).unwrap();
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[print_schema]
2+
file = "src/schema.rs"
3+
with_docs = true
4+
filter = { blacklist = ["users1"] }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[print_schema]
2+
file = "src/schema.rs"
3+
with_docs = true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[print_schema]
2+
file = "src/schema.rs"
3+
with_docs = true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[print_schema]
2+
file = "src/schema.rs"
3+
with_docs = true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[print_schema]
2+
file = "src/schema.rs"
3+
with_docs = true

0 commit comments

Comments
 (0)