bin: Improve error handling in project file lookup
Previously errors were being silently ignored. Eg, if `rustfmt` did not have permission to read a `rustfmt.toml` file, the default configuration was used without informing the user.
This commit is contained in:
parent
edcc4ec6c0
commit
98726d0a53
1 changed files with 41 additions and 28 deletions
|
@ -22,7 +22,7 @@ use rustfmt::config::{Config, WriteMode};
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, ErrorKind, Read, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use getopts::{Matches, Options};
|
use getopts::{Matches, Options};
|
||||||
|
@ -43,39 +43,55 @@ enum Operation {
|
||||||
Stdin(String, WriteMode),
|
Stdin(String, WriteMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to find a project file in the input file directory and its parents.
|
/// Try to find a project file in the given directory and its parents.
|
||||||
fn lookup_project_file(input_file: &Path) -> io::Result<PathBuf> {
|
fn lookup_project_file(dir: &Path) -> io::Result<Option<PathBuf>> {
|
||||||
let mut current = if input_file.is_relative() {
|
let mut current = if dir.is_relative() {
|
||||||
try!(env::current_dir()).join(input_file)
|
try!(env::current_dir()).join(dir)
|
||||||
} else {
|
} else {
|
||||||
input_file.to_path_buf()
|
dir.to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
||||||
current = try!(fs::canonicalize(current));
|
current = try!(fs::canonicalize(current));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let config_file = current.join("rustfmt.toml");
|
let config_file = current.join("rustfmt.toml");
|
||||||
if let Ok(md) = fs::metadata(&config_file) {
|
match fs::metadata(&config_file) {
|
||||||
|
Ok(md) => {
|
||||||
// Properly handle unlikely situation of a directory named `rustfmt.toml`.
|
// Properly handle unlikely situation of a directory named `rustfmt.toml`.
|
||||||
if md.is_file() {
|
if md.is_file() {
|
||||||
return Ok(config_file);
|
return Ok(Some(config_file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If it's not found, we continue searching; otherwise something went wrong and we
|
||||||
|
// return the error.
|
||||||
|
Err(e) => {
|
||||||
|
if e.kind() != ErrorKind::NotFound {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the current directory has no parent, we're done searching.
|
// If the current directory has no parent, we're done searching.
|
||||||
if !current.pop() {
|
if !current.pop() {
|
||||||
return Err(io::Error::new(io::ErrorKind::NotFound, "Config not found"));
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to find a project file. If it's found, read it.
|
/// Resolve the config for input in `dir`.
|
||||||
fn lookup_and_read_project_file(input_file: &Path) -> io::Result<(PathBuf, String)> {
|
///
|
||||||
let path = try!(lookup_project_file(input_file));
|
/// Returns the `Config` to use, and the path of the project file if there was
|
||||||
|
/// one.
|
||||||
|
fn resolve_config(dir: &Path) -> io::Result<(Config, Option<PathBuf>)> {
|
||||||
|
let path = try!(lookup_project_file(dir));
|
||||||
|
if path.is_none() {
|
||||||
|
return Ok((Config::default(), None));
|
||||||
|
}
|
||||||
|
let path = path.unwrap();
|
||||||
let mut file = try!(File::open(&path));
|
let mut file = try!(File::open(&path));
|
||||||
let mut toml = String::new();
|
let mut toml = String::new();
|
||||||
try!(file.read_to_string(&mut toml));
|
try!(file.read_to_string(&mut toml));
|
||||||
Ok((path, toml))
|
Ok((Config::from_toml(&toml), Some(path)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_config(config: &mut Config, matches: &Matches) {
|
fn update_config(config: &mut Config, matches: &Matches) {
|
||||||
|
@ -127,25 +143,22 @@ fn execute() -> i32 {
|
||||||
}
|
}
|
||||||
Operation::Stdin(input, write_mode) => {
|
Operation::Stdin(input, write_mode) => {
|
||||||
// try to read config from local directory
|
// try to read config from local directory
|
||||||
let config = match lookup_and_read_project_file(&Path::new(".")) {
|
let (config, _) = resolve_config(&env::current_dir().unwrap())
|
||||||
Ok((_, toml)) => Config::from_toml(&toml),
|
.expect("Error resolving config");
|
||||||
Err(_) => Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
run_from_stdin(input, write_mode, &config);
|
run_from_stdin(input, write_mode, &config);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
Operation::Format(files, write_mode) => {
|
Operation::Format(files, write_mode) => {
|
||||||
for file in files {
|
for file in files {
|
||||||
let mut config = match lookup_and_read_project_file(&file) {
|
let (mut config, path) = resolve_config(file.parent().unwrap())
|
||||||
Ok((path, toml)) => {
|
.expect(&format!("Error resolving config for {}",
|
||||||
|
file.display()));
|
||||||
|
if let Some(path) = path {
|
||||||
println!("Using rustfmt config file {} for {}",
|
println!("Using rustfmt config file {} for {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
file.display());
|
file.display());
|
||||||
Config::from_toml(&toml)
|
|
||||||
}
|
}
|
||||||
Err(_) => Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
update_config(&mut config, &matches);
|
update_config(&mut config, &matches);
|
||||||
run(&file, write_mode, &config);
|
run(&file, write_mode, &config);
|
||||||
|
|
Loading…
Reference in a new issue