Format {
files: Vec<PathBuf>,
config_path: Option<PathBuf>,
+ minimal_config_path: Option<String>,
},
/// Print the help message.
Help,
Version,
/// Print detailed configuration help.
ConfigHelp,
+ /// Output default config to a file
+ ConfigOutputDefault { path: String },
/// No file specified, read from stdin
Stdin {
input: String,
}
if let Some(ref file_lines) = matches.opt_str("file-lines") {
- options.file_lines = try!(file_lines.parse());
+ options.file_lines = file_lines.parse()?;
}
Ok(options)
}
fn apply_to(self, config: &mut Config) {
- config.skip_children = self.skip_children;
- config.verbose = self.verbose;
- config.file_lines = self.file_lines;
+ config.set().skip_children(self.skip_children);
+ config.set().verbose(self.verbose);
+ config.set().file_lines(self.file_lines);
if let Some(write_mode) = self.write_mode {
- config.write_mode = write_mode;
+ config.set().write_mode(write_mode);
}
}
}
+const CONFIG_FILE_NAMES: [&'static str; 2] = [".rustfmt.toml", "rustfmt.toml"];
+
/// Try to find a project file in the given directory and its parents. Returns the path of a the
/// nearest project file if one exists, or `None` if no project file was found.
fn lookup_project_file(dir: &Path) -> FmtResult<Option<PathBuf>> {
let mut current = if dir.is_relative() {
- try!(env::current_dir()).join(dir)
+ env::current_dir()?.join(dir)
} else {
dir.to_path_buf()
};
- current = try!(fs::canonicalize(current));
-
- const CONFIG_FILE_NAMES: [&'static str; 2] = [".rustfmt.toml", "rustfmt.toml"];
+ current = fs::canonicalize(current)?;
loop {
for config_file_name in &CONFIG_FILE_NAMES {
}
}
+fn open_config_file(file_path: &Path) -> FmtResult<(Config, Option<PathBuf>)> {
+ let mut file = File::open(&file_path)?;
+ let mut toml = String::new();
+ file.read_to_string(&mut toml)?;
+ match Config::from_toml(&toml) {
+ Ok(cfg) => Ok((cfg, Some(file_path.to_path_buf()))),
+ Err(err) => Err(FmtError::from(err)),
+ }
+}
+
/// Resolve the config for input in `dir`.
///
/// Returns the `Config` to use, and the path of the project file if there was
/// one.
fn resolve_config(dir: &Path) -> FmtResult<(Config, Option<PathBuf>)> {
- let path = try!(lookup_project_file(dir));
+ let path = 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 toml = String::new();
- try!(file.read_to_string(&mut toml));
- match Config::from_toml(&toml) {
- Ok(cfg) => Ok((cfg, Some(path))),
- Err(err) => Err(FmtError::from(err)),
- }
+ open_config_file(&path.unwrap())
}
/// read the given config file path recursively if present else read the project file path
-> FmtResult<(Config, Option<PathBuf>)> {
if let Some(config_file) = config_path {
- let (toml, path) = try!(resolve_config(config_file.as_ref()));
+ let (toml, path) = open_config_file(config_file.as_ref())?;
if path.is_some() {
return Ok((toml, path));
}
opts.optflag("",
"config-help",
"show details of rustfmt configuration options");
+ opts.optopt("",
+ "dump-default-config",
+ "Dumps the default configuration to a file and exits.",
+ "PATH");
+ opts.optopt("",
+ "dump-minimal-config",
+ "Dumps configuration options that were checked during formatting to a file.",
+ "PATH");
opts.optopt("",
"config-path",
"Recursively searches the given path for the rustfmt.toml config file. If not \
}
fn execute(opts: &Options) -> FmtResult<Summary> {
- let matches = try!(opts.parse(env::args().skip(1)));
+ let matches = opts.parse(env::args().skip(1))?;
- match try!(determine_operation(&matches)) {
+ match determine_operation(&matches)? {
Operation::Help => {
print_usage(opts, "");
+ Summary::print_exit_codes();
Ok(Summary::new())
}
Operation::Version => {
Config::print_docs();
Ok(Summary::new())
}
+ Operation::ConfigOutputDefault { path } => {
+ let mut file = File::create(path)?;
+ let toml = Config::default().all_options().to_toml()?;
+ file.write_all(toml.as_bytes())?;
+ Ok(Summary::new())
+ }
Operation::Stdin { input, config_path } => {
// try to read config from local directory
let (mut config, _) = match_cli_path_or_file(config_path,
&env::current_dir().unwrap())?;
// write_mode is always Plain for Stdin.
- config.write_mode = WriteMode::Plain;
+ config.set().write_mode(WriteMode::Plain);
// parse file_lines
if let Some(ref file_lines) = matches.opt_str("file-lines") {
- config.file_lines = try!(file_lines.parse());
- for f in config.file_lines.files() {
+ config.set().file_lines(file_lines.parse()?);
+ for f in config.file_lines().files() {
if f != "stdin" {
println!("Warning: Extra file listed in file_lines option '{}'", f);
}
Ok(run(Input::Text(input), &config))
}
- Operation::Format { files, config_path } => {
- let options = try!(CliOptions::from_matches(&matches));
+ Operation::Format {
+ files,
+ config_path,
+ minimal_config_path,
+ } => {
+ let options = CliOptions::from_matches(&matches)?;
for f in options.file_lines.files() {
if !files.contains(&PathBuf::from(f)) {
let mut path = None;
// Load the config path file if provided
if let Some(config_file) = config_path {
- let (cfg_tmp, path_tmp) = resolve_config(config_file.as_ref())?;
+ let (cfg_tmp, path_tmp) = open_config_file(config_file.as_ref())?;
config = cfg_tmp;
path = path_tmp;
};
let mut error_summary = Summary::new();
for file in files {
- // Check the file directory if the config-path could not be read or not provided
- if path.is_none() {
- let (config_tmp, path_tmp) = resolve_config(file.parent().unwrap())?;
- if options.verbose {
- if let Some(path) = path_tmp.as_ref() {
- println!("Using rustfmt config file {} for {}",
- path.display(),
- file.display());
+ if !file.exists() {
+ println!("Error: file `{}` does not exist", file.to_str().unwrap());
+ error_summary.add_operational_error();
+ } else if file.is_dir() {
+ println!("Error: `{}` is a directory", file.to_str().unwrap());
+ error_summary.add_operational_error();
+ } else {
+ // Check the file directory if the config-path could not be read or not provided
+ if path.is_none() {
+ let (config_tmp, path_tmp) = resolve_config(file.parent().unwrap())?;
+ if options.verbose {
+ if let Some(path) = path_tmp.as_ref() {
+ println!("Using rustfmt config file {} for {}",
+ path.display(),
+ file.display());
+ }
}
+ config = config_tmp;
}
- config = config_tmp;
+
+ options.clone().apply_to(&mut config);
+ error_summary.add(run(Input::File(file), &config));
}
+ }
- options.clone().apply_to(&mut config);
- error_summary.add(run(Input::File(file), &config));
+ // If we were given a path via dump-minimal-config, output any options
+ // that were used during formatting as TOML.
+ if let Some(path) = minimal_config_path {
+ let mut file = File::create(path)?;
+ let toml = config.used_options().to_toml()?;
+ file.write_all(toml.as_bytes())?;
}
+
Ok(error_summary)
}
}
}
fn print_usage(opts: &Options, reason: &str) {
- let reason = format!("{}\nusage: {} [options] <file>...",
+ let reason = format!("{}\n\nusage: {} [options] <file>...",
reason,
env::args_os().next().unwrap().to_string_lossy());
println!("{}", opts.usage(&reason));
return Ok(Operation::ConfigHelp);
}
+ if let Some(path) = matches.opt_str("dump-default-config") {
+ return Ok(Operation::ConfigOutputDefault { path });
+ }
+
if matches.opt_present("version") {
return Ok(Operation::Version);
}
+ let config_path_not_found = |path: &str| -> FmtResult<Operation> {
+ Err(FmtError::from(format!("Error: unable to find a config file for the given path: `{}`",
+ path)))
+ };
+
// Read the config_path and convert to parent dir if a file is provided.
- let config_path: Option<PathBuf> = matches
- .opt_str("config-path")
- .map(PathBuf::from)
- .and_then(|dir| {
- if dir.is_file() {
- return dir.parent().map(|v| v.into());
- }
- Some(dir)
- });
+ // If a config file cannot be found from the given path, return error.
+ let config_path: Option<PathBuf> = match matches.opt_str("config-path").map(PathBuf::from) {
+ Some(ref path) if !path.exists() => return config_path_not_found(path.to_str().unwrap()),
+ Some(ref path) if path.is_dir() => {
+ let mut config_file_path = None;
+ for config_file_name in &CONFIG_FILE_NAMES {
+ let temp_path = path.join(config_file_name);
+ if temp_path.is_file() {
+ config_file_path = Some(temp_path);
+ }
+ }
+ if config_file_path.is_some() {
+ config_file_path
+ } else {
+ return config_path_not_found(path.to_str().unwrap());
+ }
+ }
+ path @ _ => path,
+ };
+
+ // If no path is given, we won't output a minimal config.
+ let minimal_config_path = matches.opt_str("dump-minimal-config");
// if no file argument is supplied, read from stdin
if matches.free.is_empty() {
let mut buffer = String::new();
- try!(io::stdin().read_to_string(&mut buffer));
+ io::stdin().read_to_string(&mut buffer)?;
return Ok(Operation::Stdin {
input: buffer,
});
}
- let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();
+ let files: Vec<_> = matches
+ .free
+ .iter()
+ .map(|s| {
+ let p = PathBuf::from(s);
+ // we will do comparison later, so here tries to canonicalize first
+ // to get the expected behavior.
+ p.canonicalize().unwrap_or(p)
+ })
+ .collect();
Ok(Operation::Format {
files: files,
config_path: config_path,
+ minimal_config_path: minimal_config_path,
})
}