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:
Kamal Marhubi 2016-01-31 10:21:48 -05:00
parent edcc4ec6c0
commit 98726d0a53

View file

@ -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);