// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![cfg(not(test))]
-
extern crate env_logger;
+#[macro_use]
+extern crate failure;
extern crate getopts;
extern crate rustfmt_nightly as rustfmt;
+use std::env;
use std::fs::File;
-use std::io::{self, Read, Write};
+use std::io::{self, stdout, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
-use std::{env, error};
+
+use failure::err_msg;
use getopts::{Matches, Options};
-use rustfmt::config::file_lines::FileLines;
-use rustfmt::config::{get_toml_path, Color, Config, WriteMode};
-use rustfmt::{run, FileName, Input, Summary};
+use rustfmt::{
+ load_config, CliOptions, Color, Config, Edition, EmitMode, ErrorKind, FileLines, FileName,
+ Input, Session, Verbosity,
+};
-type FmtError = Box<error::Error + Send + Sync>;
-type FmtResult<T> = std::result::Result<T, FmtError>;
+fn main() {
+ env_logger::init();
+ let opts = make_opts();
+
+ let exit_code = match execute(&opts) {
+ Ok(code) => code,
+ Err(e) => {
+ eprintln!("{}", e.to_string());
+ 1
+ }
+ };
+ // Make sure standard output is flushed before we exit.
+ std::io::stdout().flush().unwrap();
+
+ // Exit with given exit code.
+ //
+ // NOTE: This immediately terminates the process without doing any cleanup,
+ // so make sure to finish all necessary cleanup before this is called.
+ std::process::exit(exit_code);
+}
/// Rustfmt operations.
enum Operation {
/// Format files and their child modules.
Format {
files: Vec<PathBuf>,
- config_path: Option<PathBuf>,
minimal_config_path: Option<String>,
},
/// Print the help message.
- Help,
+ Help(HelpOp),
// Print version information
Version,
- /// Print detailed configuration help.
- ConfigHelp,
/// Output default config to a file, or stdout if None
ConfigOutputDefault {
path: Option<String>,
/// No file specified, read from stdin
Stdin {
input: String,
- config_path: Option<PathBuf>,
},
}
-/// Parsed command line options.
-#[derive(Clone, Debug, Default)]
-struct CliOptions {
- skip_children: Option<bool>,
- verbose: bool,
- write_mode: Option<WriteMode>,
- color: Option<Color>,
- file_lines: FileLines, // Default is all lines in all files.
- unstable_features: bool,
- error_on_unformatted: Option<bool>,
-}
-
-impl CliOptions {
- fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
- let mut options = CliOptions::default();
- options.verbose = matches.opt_present("verbose");
- let unstable_features = matches.opt_present("unstable-features");
- let rust_nightly = option_env!("CFG_RELEASE_CHANNEL")
- .map(|c| c == "nightly")
- .unwrap_or(false);
- if unstable_features && !rust_nightly {
- return Err(FmtError::from(
- "Unstable features are only available on Nightly channel",
- ));
- } else {
- options.unstable_features = unstable_features;
- }
-
- if let Some(ref write_mode) = matches.opt_str("write-mode") {
- if let Ok(write_mode) = WriteMode::from_str(write_mode) {
- options.write_mode = Some(write_mode);
- } else {
- return Err(FmtError::from(format!(
- "Invalid write-mode: {}",
- write_mode
- )));
- }
- }
-
- if let Some(ref color) = matches.opt_str("color") {
- match Color::from_str(color) {
- Ok(color) => options.color = Some(color),
- _ => return Err(FmtError::from(format!("Invalid color: {}", color))),
- }
- }
-
- if let Some(ref file_lines) = matches.opt_str("file-lines") {
- options.file_lines = file_lines.parse()?;
- }
-
- if matches.opt_present("skip-children") {
- options.skip_children = Some(true);
- }
- if matches.opt_present("error-on-unformatted") {
- options.error_on_unformatted = Some(true);
- }
-
- Ok(options)
- }
-
- fn apply_to(self, config: &mut Config) {
- config.set().verbose(self.verbose);
- config.set().file_lines(self.file_lines);
- config.set().unstable_features(self.unstable_features);
- if let Some(skip_children) = self.skip_children {
- config.set().skip_children(skip_children);
- }
- if let Some(error_on_unformatted) = self.error_on_unformatted {
- config.set().error_on_unformatted(error_on_unformatted);
- }
- if let Some(write_mode) = self.write_mode {
- config.set().write_mode(write_mode);
- }
- if let Some(color) = self.color {
- config.set().color(color);
- }
- }
-}
-
-/// read the given config file path recursively if present else read the project file path
-fn match_cli_path_or_file(
- config_path: Option<PathBuf>,
- input_file: &Path,
-) -> FmtResult<(Config, Option<PathBuf>)> {
- if let Some(config_file) = config_path {
- let toml = Config::from_toml_path(config_file.as_ref())?;
- return Ok((toml, Some(config_file)));
- }
- Config::from_resolved_toml_path(input_file).map_err(FmtError::from)
+/// Arguments to `--help`
+enum HelpOp {
+ None,
+ Config,
+ FileLines,
}
fn make_opts() -> Options {
let mut opts = Options::new();
- // Sorted in alphabetical order.
- opts.optopt(
- "",
- "color",
- "Use colored output (if supported)",
- "[always|never|auto]",
- );
opts.optflag(
"",
- "config-help",
- "Show details of rustfmt configuration options",
+ "check",
+ "Run in 'check' mode. Exits with 0 if input is formatted correctly. Exits \
+ with 1 and prints a diff if formatting is required.",
);
+ let is_nightly = is_nightly();
+ let emit_opts = if is_nightly {
+ "[files|stdout|coverage|checkstyle]"
+ } else {
+ "[files|stdout]"
+ };
+ opts.optopt("", "emit", "What data to emit and how", emit_opts);
+ opts.optflag("", "backup", "Backup any modified files.");
opts.optopt(
"",
"config-path",
found reverts to the input file path",
"[Path for the configuration file]",
);
- opts.opt(
- "",
- "dump-default-config",
- "Dumps default configuration to PATH. PATH defaults to stdout, if omitted.",
- "PATH",
- getopts::HasArg::Maybe,
- getopts::Occur::Optional,
- );
+ opts.optopt("", "edition", "Rust edition to use", "[2015|2018]");
opts.optopt(
"",
- "dump-minimal-config",
- "Dumps configuration options that were checked during formatting to a file.",
- "PATH",
- );
- opts.optflag(
- "",
- "error-on-unformatted",
- "Error if unable to get comments or string literals within max_width, \
- or they are left with trailing whitespaces",
+ "color",
+ "Use colored output (if supported)",
+ "[always|never|auto]",
);
opts.optopt(
"",
- "file-lines",
- "Format specified line ranges. See README for more detail on the JSON format.",
- "JSON",
- );
- opts.optflag("h", "help", "Show this message");
- opts.optflag("", "skip-children", "Don't reformat child modules");
- opts.optflag(
- "",
- "unstable-features",
- "Enables unstable features. Only available on nightly channel",
+ "print-config",
+ "Dumps a default or minimal config to PATH. A minimal config is the \
+ subset of the current config file used for formatting the current program.",
+ "[minimal|default] PATH",
);
+
+ if is_nightly {
+ opts.optflag(
+ "",
+ "unstable-features",
+ "Enables unstable features. Only available on nightly channel.",
+ );
+ opts.optopt(
+ "",
+ "file-lines",
+ "Format specified line ranges. Run with `--help=file-lines` for \
+ more detail (unstable).",
+ "JSON",
+ );
+ opts.optflag(
+ "",
+ "error-on-unformatted",
+ "Error if unable to get comments or string literals within max_width, \
+ or they are left with trailing whitespaces (unstable).",
+ );
+ opts.optflag(
+ "",
+ "skip-children",
+ "Don't reformat child modules (unstable).",
+ );
+ }
+
opts.optflag("v", "verbose", "Print verbose output");
+ opts.optflag("q", "quiet", "Print less output");
opts.optflag("V", "version", "Show version information");
- opts.optopt(
- "",
- "write-mode",
- "How to write output (not usable when piping from stdin)",
- "[replace|overwrite|display|plain|diff|coverage|checkstyle]",
+ opts.optflagopt(
+ "h",
+ "help",
+ "Show this message or help about a specific topic: `config` or `file-lines`",
+ "=TOPIC",
);
opts
}
-fn execute(opts: &Options) -> FmtResult<Summary> {
+fn is_nightly() -> bool {
+ option_env!("CFG_RELEASE_CHANNEL").map_or(false, |c| c == "nightly" || c == "dev")
+}
+
+// Returned i32 is an exit code
+fn execute(opts: &Options) -> Result<i32, failure::Error> {
let matches = opts.parse(env::args().skip(1))?;
+ let options = GetOptsOptions::from_matches(&matches)?;
match determine_operation(&matches)? {
- Operation::Help => {
+ Operation::Help(HelpOp::None) => {
print_usage_to_stdout(opts, "");
- Summary::print_exit_codes();
- Ok(Summary::default())
+ Ok(0)
+ }
+ Operation::Help(HelpOp::Config) => {
+ Config::print_docs(&mut stdout(), options.unstable_features);
+ Ok(0)
+ }
+ Operation::Help(HelpOp::FileLines) => {
+ print_help_file_lines();
+ Ok(0)
}
Operation::Version => {
print_version();
- Ok(Summary::default())
- }
- Operation::ConfigHelp => {
- Config::print_docs();
- Ok(Summary::default())
+ Ok(0)
}
Operation::ConfigOutputDefault { path } => {
- let toml = Config::default().all_options().to_toml()?;
+ let toml = Config::default().all_options().to_toml().map_err(err_msg)?;
if let Some(path) = path {
let mut file = File::create(path)?;
file.write_all(toml.as_bytes())?;
} else {
io::stdout().write_all(toml.as_bytes())?;
}
- Ok(Summary::default())
- }
- 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.set().write_mode(WriteMode::Plain);
-
- // parse file_lines
- if let Some(ref file_lines) = matches.opt_str("file-lines") {
- config.set().file_lines(file_lines.parse()?);
- for f in config.file_lines().files() {
- match *f {
- FileName::Custom(ref f) if f == "stdin" => {}
- _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f),
- }
- }
- }
-
- let mut error_summary = Summary::default();
- if config.version_meets_requirement(&mut error_summary) {
- error_summary.add(run(Input::Text(input), &config));
- }
-
- Ok(error_summary)
+ Ok(0)
}
+ Operation::Stdin { input } => format_string(input, options),
Operation::Format {
files,
- config_path,
minimal_config_path,
- } => {
- let options = CliOptions::from_matches(&matches)?;
-
- for f in options.file_lines.files() {
- match *f {
- FileName::Real(ref f) if files.contains(f) => {}
- FileName::Real(_) => {
- eprintln!("Warning: Extra file listed in file_lines option '{}'", f)
- }
- _ => eprintln!("Warning: Not a file '{}'", f),
- }
- }
-
- let mut config = Config::default();
- // Load the config path file if provided
- if let Some(config_file) = config_path.as_ref() {
- config = Config::from_toml_path(config_file.as_ref())?;
- };
+ } => format(files, minimal_config_path, &options),
+ }
+}
- if options.verbose {
- if let Some(path) = config_path.as_ref() {
- println!("Using rustfmt config file {}", path.display());
- }
- }
+fn format_string(input: String, options: GetOptsOptions) -> Result<i32, failure::Error> {
+ // try to read config from local directory
+ let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?;
- let mut error_summary = Summary::default();
- for file in files {
- if !file.exists() {
- eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
- error_summary.add_operational_error();
- } else if file.is_dir() {
- eprintln!("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 config_path.is_none() {
- let (config_tmp, path_tmp) =
- Config::from_resolved_toml_path(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;
- }
+ // emit mode is always Stdout for Stdin.
+ config.set().emit_mode(EmitMode::Stdout);
+ config.set().verbose(Verbosity::Quiet);
- if !config.version_meets_requirement(&mut error_summary) {
- break;
- }
+ // parse file_lines
+ config.set().file_lines(options.file_lines);
+ for f in config.file_lines().files() {
+ match *f {
+ FileName::Stdin => {}
+ _ => eprintln!("Warning: Extra file listed in file_lines option '{}'", f),
+ }
+ }
- options.clone().apply_to(&mut config);
- error_summary.add(run(Input::File(file), &config));
- }
- }
+ let out = &mut stdout();
+ let mut session = Session::new(config, Some(out));
+ format_and_emit_report(&mut session, Input::Text(input));
- // 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())?;
- }
+ let exit_code = if session.has_operational_errors() || session.has_parsing_errors() {
+ 1
+ } else {
+ 0
+ };
+ Ok(exit_code)
+}
- Ok(error_summary)
+fn format(
+ files: Vec<PathBuf>,
+ minimal_config_path: Option<String>,
+ options: &GetOptsOptions,
+) -> Result<i32, failure::Error> {
+ options.verify_file_lines(&files);
+ let (config, config_path) = load_config(None, Some(options.clone()))?;
+
+ if config.verbose() == Verbosity::Verbose {
+ if let Some(path) = config_path.as_ref() {
+ println!("Using rustfmt config file {}", path.display());
}
}
-}
-fn main() {
- env_logger::init();
+ let out = &mut stdout();
+ let mut session = Session::new(config, Some(out));
- let opts = make_opts();
+ for file in files {
+ if !file.exists() {
+ eprintln!("Error: file `{}` does not exist", file.to_str().unwrap());
+ session.add_operational_error();
+ } else if file.is_dir() {
+ eprintln!("Error: `{}` is a directory", file.to_str().unwrap());
+ session.add_operational_error();
+ } else {
+ // Check the file directory if the config-path could not be read or not provided
+ if config_path.is_none() {
+ let (local_config, config_path) =
+ load_config(Some(file.parent().unwrap()), Some(options.clone()))?;
+ if local_config.verbose() == Verbosity::Verbose {
+ if let Some(path) = config_path {
+ println!(
+ "Using rustfmt config file {} for {}",
+ path.display(),
+ file.display()
+ );
+ }
+ }
- let exit_code = match execute(&opts) {
- Ok(summary) => {
- if summary.has_operational_errors() {
- 1
- } else if summary.has_parsing_errors() {
- 2
- } else if summary.has_formatting_errors() {
- 3
- } else if summary.has_diff {
- // should only happen in diff mode
- 4
+ session.override_config(local_config, |sess| {
+ format_and_emit_report(sess, Input::File(file))
+ });
} else {
- assert!(summary.has_no_errors());
- 0
+ format_and_emit_report(&mut session, Input::File(file));
}
}
- Err(e) => {
- eprintln!("{}", e.to_string());
- 1
- }
+ }
+
+ // 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 = session.config.used_options().to_toml().map_err(err_msg)?;
+ file.write_all(toml.as_bytes())?;
+ }
+
+ let exit_code = if session.has_operational_errors()
+ || session.has_parsing_errors()
+ || ((session.has_diff() || session.has_check_errors()) && options.check)
+ {
+ 1
+ } else {
+ 0
};
- // Make sure standard output is flushed before we exit.
- std::io::stdout().flush().unwrap();
+ Ok(exit_code)
+}
- // Exit with given exit code.
- //
- // NOTE: This immediately terminates the process without doing any cleanup,
- // so make sure to finish all necessary cleanup before this is called.
- std::process::exit(exit_code);
+fn format_and_emit_report<T: Write>(session: &mut Session<T>, input: Input) {
+ match session.format(input) {
+ Ok(report) => {
+ if report.has_warnings() {
+ match term::stderr() {
+ Some(ref t)
+ if session.config.color().use_colored_tty()
+ && t.supports_color()
+ && t.supports_attr(term::Attr::Bold) =>
+ {
+ match report.fancy_print(term::stderr().unwrap()) {
+ Ok(..) => (),
+ Err(..) => panic!("Unable to write to stderr: {}", report),
+ }
+ }
+ _ => eprintln!("{}", report),
+ }
+ }
+ }
+ Err(msg) => {
+ eprintln!("Error writing files: {}", msg);
+ session.add_operational_error();
+ }
+ }
}
fn print_usage_to_stdout(opts: &Options, reason: &str) {
println!("{}", opts.usage(&msg));
}
+fn print_help_file_lines() {
+ println!(
+ "If you want to restrict reformatting to specific sets of lines, you can
+use the `--file-lines` option. Its argument is a JSON array of objects
+with `file` and `range` properties, where `file` is a file name, and
+`range` is an array representing a range of lines like `[7,13]`. Ranges
+are 1-based and inclusive of both end points. Specifying an empty array
+will result in no files being formatted. For example,
+
+```
+rustfmt --file-lines '[
+ {{\"file\":\"src/lib.rs\",\"range\":[7,13]}},
+ {{\"file\":\"src/lib.rs\",\"range\":[21,29]}},
+ {{\"file\":\"src/foo.rs\",\"range\":[10,11]}},
+ {{\"file\":\"src/foo.rs\",\"range\":[15,15]}}]'
+```
+
+would format lines `7-13` and `21-29` of `src/lib.rs`, and lines `10-11`,
+and `15` of `src/foo.rs`. No other files would be formatted, even if they
+are included as out of line modules from `src/lib.rs`."
+ );
+}
+
fn print_version() {
let version_info = format!(
"{}-{}",
println!("rustfmt {}", version_info);
}
-fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
+fn determine_operation(matches: &Matches) -> Result<Operation, ErrorKind> {
if matches.opt_present("h") {
- return Ok(Operation::Help);
- }
-
- if matches.opt_present("config-help") {
- return Ok(Operation::ConfigHelp);
+ let topic = matches.opt_str("h");
+ if topic == None {
+ return Ok(Operation::Help(HelpOp::None));
+ } else if topic == Some("config".to_owned()) {
+ return Ok(Operation::Help(HelpOp::Config));
+ } else if topic == Some("file-lines".to_owned()) {
+ return Ok(Operation::Help(HelpOp::FileLines));
+ } else {
+ println!("Unknown help topic: `{}`\n", topic.unwrap());
+ return Ok(Operation::Help(HelpOp::None));
+ }
}
- if matches.opt_present("dump-default-config") {
- // NOTE for some reason when configured with HasArg::Maybe + Occur::Optional opt_default
- // doesn't recognize `--foo bar` as a long flag with an argument but as a long flag with no
- // argument *plus* a free argument. Thus we check for that case in this branch -- this is
- // required for backward compatibility.
- if let Some(path) = matches.free.get(0) {
- return Ok(Operation::ConfigOutputDefault {
- path: Some(path.clone()),
- });
- } else {
- return Ok(Operation::ConfigOutputDefault {
- path: matches.opt_str("dump-default-config"),
- });
+ let mut minimal_config_path = None;
+ if let Some(ref kind) = matches.opt_str("print-config") {
+ let path = matches.free.get(0).cloned();
+ if kind == "default" {
+ return Ok(Operation::ConfigOutputDefault { path });
+ } else if kind == "minimal" {
+ minimal_config_path = path;
+ if minimal_config_path.is_none() {
+ println!("WARNING: PATH required for `--print-config minimal`");
+ }
}
}
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.
- // 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 config_file_path = get_toml_path(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();
io::stdin().read_to_string(&mut buffer)?;
- return Ok(Operation::Stdin {
- input: buffer,
- config_path,
- });
+ return Ok(Operation::Stdin { input: buffer });
}
let files: Vec<_> = matches
Ok(Operation::Format {
files,
- config_path,
minimal_config_path,
})
}
+
+const STABLE_EMIT_MODES: [EmitMode; 3] = [EmitMode::Files, EmitMode::Stdout, EmitMode::Diff];
+
+/// Parsed command line options.
+#[derive(Clone, Debug, Default)]
+struct GetOptsOptions {
+ skip_children: Option<bool>,
+ quiet: bool,
+ verbose: bool,
+ config_path: Option<PathBuf>,
+ emit_mode: EmitMode,
+ backup: bool,
+ check: bool,
+ edition: Option<Edition>,
+ color: Option<Color>,
+ file_lines: FileLines, // Default is all lines in all files.
+ unstable_features: bool,
+ error_on_unformatted: Option<bool>,
+}
+
+impl GetOptsOptions {
+ pub fn from_matches(matches: &Matches) -> Result<GetOptsOptions, failure::Error> {
+ let mut options = GetOptsOptions::default();
+ options.verbose = matches.opt_present("verbose");
+ options.quiet = matches.opt_present("quiet");
+ if options.verbose && options.quiet {
+ return Err(format_err!("Can't use both `--verbose` and `--quiet`"));
+ }
+
+ let rust_nightly = is_nightly();
+
+ if rust_nightly {
+ options.unstable_features = matches.opt_present("unstable-features");
+
+ if options.unstable_features {
+ if matches.opt_present("skip-children") {
+ options.skip_children = Some(true);
+ }
+ if matches.opt_present("error-on-unformatted") {
+ options.error_on_unformatted = Some(true);
+ }
+ if let Some(ref file_lines) = matches.opt_str("file-lines") {
+ options.file_lines = file_lines.parse().map_err(err_msg)?;
+ }
+ } else {
+ let mut unstable_options = vec![];
+ if matches.opt_present("skip-children") {
+ unstable_options.push("`--skip-children`");
+ }
+ if matches.opt_present("error-on-unformatted") {
+ unstable_options.push("`--error-on-unformatted`");
+ }
+ if matches.opt_present("file-lines") {
+ unstable_options.push("`--file-lines`");
+ }
+ if !unstable_options.is_empty() {
+ let s = if unstable_options.len() == 1 { "" } else { "s" };
+ return Err(format_err!(
+ "Unstable option{} ({}) used without `--unstable-features`",
+ s,
+ unstable_options.join(", "),
+ ));
+ }
+ }
+ }
+
+ options.config_path = matches.opt_str("config-path").map(PathBuf::from);
+
+ options.check = matches.opt_present("check");
+ if let Some(ref emit_str) = matches.opt_str("emit") {
+ if options.check {
+ return Err(format_err!("Invalid to use `--emit` and `--check`"));
+ }
+
+ options.emit_mode = emit_mode_from_emit_str(emit_str)?;
+ }
+
+ if let Some(ref edition_str) = matches.opt_str("edition") {
+ options.edition = Some(edition_from_edition_str(edition_str)?);
+ }
+
+ if matches.opt_present("backup") {
+ options.backup = true;
+ }
+
+ if !rust_nightly {
+ if !STABLE_EMIT_MODES.contains(&options.emit_mode) {
+ return Err(format_err!(
+ "Invalid value for `--emit` - using an unstable \
+ value without `--unstable-features`",
+ ));
+ }
+ }
+
+ if let Some(ref color) = matches.opt_str("color") {
+ match Color::from_str(color) {
+ Ok(color) => options.color = Some(color),
+ _ => return Err(format_err!("Invalid color: {}", color)),
+ }
+ }
+
+ Ok(options)
+ }
+
+ fn verify_file_lines(&self, files: &[PathBuf]) {
+ for f in self.file_lines.files() {
+ match *f {
+ FileName::Real(ref f) if files.contains(f) => {}
+ FileName::Real(_) => {
+ eprintln!("Warning: Extra file listed in file_lines option '{}'", f)
+ }
+ FileName::Stdin => eprintln!("Warning: Not a file '{}'", f),
+ }
+ }
+ }
+}
+
+impl CliOptions for GetOptsOptions {
+ fn apply_to(self, config: &mut Config) {
+ if self.verbose {
+ config.set().verbose(Verbosity::Verbose);
+ } else if self.quiet {
+ config.set().verbose(Verbosity::Quiet);
+ } else {
+ config.set().verbose(Verbosity::Normal);
+ }
+ config.set().file_lines(self.file_lines);
+ config.set().unstable_features(self.unstable_features);
+ if let Some(skip_children) = self.skip_children {
+ config.set().skip_children(skip_children);
+ }
+ if let Some(error_on_unformatted) = self.error_on_unformatted {
+ config.set().error_on_unformatted(error_on_unformatted);
+ }
+ if let Some(edition) = self.edition {
+ config.set().edition(edition);
+ }
+ if self.check {
+ config.set().emit_mode(EmitMode::Diff);
+ } else {
+ config.set().emit_mode(self.emit_mode);
+ }
+ if self.backup {
+ config.set().make_backup(true);
+ }
+ if let Some(color) = self.color {
+ config.set().color(color);
+ }
+ }
+
+ fn config_path(&self) -> Option<&Path> {
+ self.config_path.as_ref().map(|p| &**p)
+ }
+}
+
+fn edition_from_edition_str(edition_str: &str) -> Result<Edition, failure::Error> {
+ match edition_str {
+ "2015" => Ok(Edition::Edition2015),
+ "2018" => Ok(Edition::Edition2018),
+ _ => Err(format_err!("Invalid value for `--edition`")),
+ }
+}
+
+fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode, failure::Error> {
+ match emit_str {
+ "files" => Ok(EmitMode::Files),
+ "stdout" => Ok(EmitMode::Stdout),
+ "coverage" => Ok(EmitMode::Coverage),
+ "checkstyle" => Ok(EmitMode::Checkstyle),
+ _ => Err(format_err!("Invalid value for `--emit`")),
+ }
+}