Refactor to make a sensible public API
0.5 - lots of breaking changes cc #2639
This commit is contained in:
parent
6a31741eaa
commit
ca610d35b3
16 changed files with 315 additions and 296 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -451,7 +451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustfmt-nightly"
|
name = "rustfmt-nightly"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
|
|
||||||
name = "rustfmt-nightly"
|
name = "rustfmt-nightly"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
|
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
|
||||||
description = "Tool to find and fix Rust formatting issues"
|
description = "Tool to find and fix Rust formatting issues"
|
||||||
repository = "https://github.com/rust-lang-nursery/rustfmt"
|
repository = "https://github.com/rust-lang-nursery/rustfmt"
|
||||||
|
|
@ -49,8 +49,8 @@ cargo_metadata = "0.5.1"
|
||||||
rustc-ap-syntax = "103.0.0"
|
rustc-ap-syntax = "103.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.0.0"
|
|
||||||
assert_cli = "0.5"
|
assert_cli = "0.5"
|
||||||
|
lazy_static = "1.0.0"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2.11"
|
libc = "0.2.11"
|
||||||
|
|
|
||||||
214
src/bin/main.rs
214
src/bin/main.rs
|
|
@ -14,23 +14,16 @@ extern crate env_logger;
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
extern crate rustfmt_nightly as rustfmt;
|
extern crate rustfmt_nightly as rustfmt;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, stdout, Read, Write};
|
use std::io::{self, stdout, Read, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
|
||||||
use std::{env, error};
|
|
||||||
|
|
||||||
use getopts::{Matches, Options};
|
use getopts::{Matches, Options};
|
||||||
|
|
||||||
use rustfmt::checkstyle;
|
use rustfmt::{emit_post_matter, emit_pre_matter, load_config, CliOptions, Config, FmtResult,
|
||||||
use rustfmt::config::file_lines::FileLines;
|
WriteMode, WRITE_MODE_LIST};
|
||||||
use rustfmt::config::{get_toml_path, Color, Config, WriteMode};
|
use rustfmt::{format_and_emit_report, FileName, Input, Summary};
|
||||||
use rustfmt::{run, FileName, Input, Summary};
|
|
||||||
|
|
||||||
type FmtError = Box<error::Error + Send + Sync>;
|
|
||||||
type FmtResult<T> = std::result::Result<T, FmtError>;
|
|
||||||
|
|
||||||
const WRITE_MODE_LIST: &str = "[replace|overwrite|display|plain|diff|coverage|checkstyle|check]";
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
@ -66,7 +59,6 @@ enum Operation {
|
||||||
/// Format files and their child modules.
|
/// Format files and their child modules.
|
||||||
Format {
|
Format {
|
||||||
files: Vec<PathBuf>,
|
files: Vec<PathBuf>,
|
||||||
config_path: Option<PathBuf>,
|
|
||||||
minimal_config_path: Option<String>,
|
minimal_config_path: Option<String>,
|
||||||
},
|
},
|
||||||
/// Print the help message.
|
/// Print the help message.
|
||||||
|
|
@ -82,105 +74,9 @@ enum Operation {
|
||||||
/// No file specified, read from stdin
|
/// No file specified, read from stdin
|
||||||
Stdin {
|
Stdin {
|
||||||
input: String,
|
input: String,
|
||||||
config_path: Option<PathBuf>,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsed command line options.
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
struct CliOptions {
|
|
||||||
skip_children: Option<bool>,
|
|
||||||
verbose: bool,
|
|
||||||
verbose_diff: 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");
|
|
||||||
options.verbose_diff = matches.opt_present("verbose-diff");
|
|
||||||
|
|
||||||
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: {}, expected one of {}",
|
|
||||||
write_mode, WRITE_MODE_LIST
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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().verbose_diff(self.verbose_diff);
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_opts() -> Options {
|
fn make_opts() -> Options {
|
||||||
let mut opts = Options::new();
|
let mut opts = Options::new();
|
||||||
|
|
||||||
|
|
@ -280,10 +176,10 @@ fn execute(opts: &Options) -> FmtResult<(WriteMode, Summary)> {
|
||||||
}
|
}
|
||||||
Ok((WriteMode::None, Summary::default()))
|
Ok((WriteMode::None, Summary::default()))
|
||||||
}
|
}
|
||||||
Operation::Stdin { input, config_path } => {
|
Operation::Stdin { input } => {
|
||||||
// try to read config from local directory
|
// try to read config from local directory
|
||||||
let (mut config, _) =
|
let options = CliOptions::from_matches(&matches)?;
|
||||||
match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;
|
let (mut config, _) = load_config(None, Some(&options))?;
|
||||||
|
|
||||||
// write_mode is always Plain for Stdin.
|
// write_mode is always Plain for Stdin.
|
||||||
config.set().write_mode(WriteMode::Plain);
|
config.set().write_mode(WriteMode::Plain);
|
||||||
|
|
@ -300,57 +196,40 @@ fn execute(opts: &Options) -> FmtResult<(WriteMode, Summary)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut error_summary = Summary::default();
|
let mut error_summary = Summary::default();
|
||||||
if config.version_meets_requirement(&mut error_summary) {
|
emit_pre_matter(&config)?;
|
||||||
let mut out = &mut stdout();
|
match format_and_emit_report(Input::Text(input), &config) {
|
||||||
checkstyle::output_header(&mut out, config.write_mode())?;
|
Ok(summary) => error_summary.add(summary),
|
||||||
error_summary.add(run(Input::Text(input), &config));
|
Err(_) => error_summary.add_operational_error(),
|
||||||
checkstyle::output_footer(&mut out, config.write_mode())?;
|
|
||||||
}
|
}
|
||||||
|
emit_post_matter(&config)?;
|
||||||
|
|
||||||
Ok((WriteMode::Plain, error_summary))
|
Ok((WriteMode::Plain, error_summary))
|
||||||
}
|
}
|
||||||
Operation::Format {
|
Operation::Format {
|
||||||
files,
|
files,
|
||||||
config_path,
|
|
||||||
minimal_config_path,
|
minimal_config_path,
|
||||||
} => {
|
} => {
|
||||||
let options = CliOptions::from_matches(&matches)?;
|
let options = CliOptions::from_matches(&matches)?;
|
||||||
format(files, config_path, minimal_config_path, options)
|
format(files, minimal_config_path, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format(
|
fn format(
|
||||||
files: Vec<PathBuf>,
|
files: Vec<PathBuf>,
|
||||||
config_path: Option<PathBuf>,
|
|
||||||
minimal_config_path: Option<String>,
|
minimal_config_path: Option<String>,
|
||||||
options: CliOptions,
|
options: CliOptions,
|
||||||
) -> FmtResult<(WriteMode, Summary)> {
|
) -> FmtResult<(WriteMode, Summary)> {
|
||||||
for f in options.file_lines.files() {
|
options.verify_file_lines(&files);
|
||||||
match *f {
|
let (config, config_path) = load_config(None, Some(&options))?;
|
||||||
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();
|
if config.verbose() {
|
||||||
// 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())?;
|
|
||||||
};
|
|
||||||
|
|
||||||
if options.verbose {
|
|
||||||
if let Some(path) = config_path.as_ref() {
|
if let Some(path) = config_path.as_ref() {
|
||||||
println!("Using rustfmt config file {}", path.display());
|
println!("Using rustfmt config file {}", path.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let write_mode = config.write_mode();
|
emit_pre_matter(&config)?;
|
||||||
let mut out = &mut stdout();
|
|
||||||
checkstyle::output_header(&mut out, write_mode)?;
|
|
||||||
let mut error_summary = Summary::default();
|
let mut error_summary = Summary::default();
|
||||||
|
|
||||||
for file in files {
|
for file in files {
|
||||||
|
|
@ -362,11 +241,11 @@ fn format(
|
||||||
error_summary.add_operational_error();
|
error_summary.add_operational_error();
|
||||||
} else {
|
} else {
|
||||||
// Check the file directory if the config-path could not be read or not provided
|
// Check the file directory if the config-path could not be read or not provided
|
||||||
if config_path.is_none() {
|
let local_config = if config_path.is_none() {
|
||||||
let (config_tmp, path_tmp) =
|
let (local_config, config_path) =
|
||||||
Config::from_resolved_toml_path(file.parent().unwrap())?;
|
load_config(Some(file.parent().unwrap()), Some(&options))?;
|
||||||
if options.verbose {
|
if local_config.verbose() {
|
||||||
if let Some(path) = path_tmp.as_ref() {
|
if let Some(path) = config_path {
|
||||||
println!(
|
println!(
|
||||||
"Using rustfmt config file {} for {}",
|
"Using rustfmt config file {} for {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
|
|
@ -374,18 +253,21 @@ fn format(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config = config_tmp;
|
local_config
|
||||||
}
|
} else {
|
||||||
|
config.clone()
|
||||||
|
};
|
||||||
|
|
||||||
if !config.version_meets_requirement(&mut error_summary) {
|
match format_and_emit_report(Input::File(file), &local_config) {
|
||||||
|
Ok(summary) => error_summary.add(summary),
|
||||||
|
Err(_) => {
|
||||||
|
error_summary.add_operational_error();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.clone().apply_to(&mut config);
|
|
||||||
error_summary.add(run(Input::File(file), &config));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkstyle::output_footer(&mut out, write_mode)?;
|
}
|
||||||
|
emit_post_matter(&config)?;
|
||||||
|
|
||||||
// If we were given a path via dump-minimal-config, output any options
|
// If we were given a path via dump-minimal-config, output any options
|
||||||
// that were used during formatting as TOML.
|
// that were used during formatting as TOML.
|
||||||
|
|
@ -395,7 +277,7 @@ fn format(
|
||||||
file.write_all(toml.as_bytes())?;
|
file.write_all(toml.as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((write_mode, error_summary))
|
Ok((config.write_mode(), error_summary))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_usage_to_stdout(opts: &Options, reason: &str) {
|
fn print_usage_to_stdout(opts: &Options, reason: &str) {
|
||||||
|
|
@ -451,28 +333,6 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
|
||||||
return Ok(Operation::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.
|
|
||||||
// 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.
|
// If no path is given, we won't output a minimal config.
|
||||||
let minimal_config_path = matches.opt_str("dump-minimal-config");
|
let minimal_config_path = matches.opt_str("dump-minimal-config");
|
||||||
|
|
||||||
|
|
@ -481,10 +341,7 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
io::stdin().read_to_string(&mut buffer)?;
|
io::stdin().read_to_string(&mut buffer)?;
|
||||||
|
|
||||||
return Ok(Operation::Stdin {
|
return Ok(Operation::Stdin { input: buffer });
|
||||||
input: buffer,
|
|
||||||
config_path,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let files: Vec<_> = matches
|
let files: Vec<_> = matches
|
||||||
|
|
@ -500,7 +357,6 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
|
||||||
|
|
||||||
Ok(Operation::Format {
|
Ok(Operation::Format {
|
||||||
files,
|
files,
|
||||||
config_path,
|
|
||||||
minimal_config_path,
|
minimal_config_path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,33 +11,26 @@
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use config::WriteMode;
|
|
||||||
use rustfmt_diff::{DiffLine, Mismatch};
|
use rustfmt_diff::{DiffLine, Mismatch};
|
||||||
|
|
||||||
pub fn output_header<T>(out: &mut T, mode: WriteMode) -> Result<(), io::Error>
|
pub fn output_header<T>(out: &mut T) -> Result<(), io::Error>
|
||||||
where
|
where
|
||||||
T: Write,
|
T: Write,
|
||||||
{
|
{
|
||||||
if mode == WriteMode::Checkstyle {
|
|
||||||
let mut xml_heading = String::new();
|
let mut xml_heading = String::new();
|
||||||
xml_heading.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
xml_heading.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||||
xml_heading.push_str("\n");
|
xml_heading.push_str("\n");
|
||||||
xml_heading.push_str("<checkstyle version=\"4.3\">");
|
xml_heading.push_str("<checkstyle version=\"4.3\">");
|
||||||
write!(out, "{}", xml_heading)?;
|
write!(out, "{}", xml_heading)
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_footer<T>(out: &mut T, mode: WriteMode) -> Result<(), io::Error>
|
pub fn output_footer<T>(out: &mut T) -> Result<(), io::Error>
|
||||||
where
|
where
|
||||||
T: Write,
|
T: Write,
|
||||||
{
|
{
|
||||||
if mode == WriteMode::Checkstyle {
|
|
||||||
let mut xml_tail = String::new();
|
let mut xml_tail = String::new();
|
||||||
xml_tail.push_str("</checkstyle>\n");
|
xml_tail.push_str("</checkstyle>\n");
|
||||||
write!(out, "{}", xml_tail)?;
|
write!(out, "{}", xml_tail)
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_checkstyle_file<T>(
|
pub fn output_checkstyle_file<T>(
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ macro_rules! is_nightly_channel {
|
||||||
|
|
||||||
macro_rules! create_config {
|
macro_rules! create_config {
|
||||||
($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
|
($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
|
||||||
|
#[cfg(test)]
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
|
@ -150,7 +151,7 @@ macro_rules! create_config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn version_meets_requirement(&self, error_summary: &mut Summary) -> bool {
|
pub(crate) fn version_meets_requirement(&self) -> bool {
|
||||||
if self.was_set().required_version() {
|
if self.was_set().required_version() {
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
let required_version = self.required_version();
|
let required_version = self.required_version();
|
||||||
|
|
@ -160,7 +161,6 @@ macro_rules! create_config {
|
||||||
version,
|
version,
|
||||||
required_version,
|
required_version,
|
||||||
);
|
);
|
||||||
error_summary.add_formatting_error();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +207,8 @@ macro_rules! create_config {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a hash set initialized with every user-facing config option name.
|
/// Returns a hash set initialized with every user-facing config option name.
|
||||||
pub fn hash_set() -> HashSet<String> {
|
#[cfg(test)]
|
||||||
|
pub(crate) fn hash_set() -> HashSet<String> {
|
||||||
let mut hash_set = HashSet::new();
|
let mut hash_set = HashSet::new();
|
||||||
$(
|
$(
|
||||||
hash_set.insert(stringify!($i).to_owned());
|
hash_set.insert(stringify!($i).to_owned());
|
||||||
|
|
@ -215,7 +216,7 @@ macro_rules! create_config {
|
||||||
hash_set
|
hash_set
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_name(name: &str) -> bool {
|
pub(crate) fn is_valid_name(name: &str) -> bool {
|
||||||
match name {
|
match name {
|
||||||
$(
|
$(
|
||||||
stringify!($i) => true,
|
stringify!($i) => true,
|
||||||
|
|
@ -224,7 +225,7 @@ macro_rules! create_config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
|
pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> {
|
||||||
let parsed: ::toml::Value =
|
let parsed: ::toml::Value =
|
||||||
toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
|
toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
|
||||||
let mut err: String = String::new();
|
let mut err: String = String::new();
|
||||||
|
|
@ -304,7 +305,7 @@ macro_rules! create_config {
|
||||||
///
|
///
|
||||||
/// Return a `Config` if the config could be read and parsed from
|
/// Return a `Config` if the config could be read and parsed from
|
||||||
/// the file, Error otherwise.
|
/// the file, Error otherwise.
|
||||||
pub fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
|
pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> {
|
||||||
let mut file = File::open(&file_path)?;
|
let mut file = File::open(&file_path)?;
|
||||||
let mut toml = String::new();
|
let mut toml = String::new();
|
||||||
file.read_to_string(&mut toml)?;
|
file.read_to_string(&mut toml)?;
|
||||||
|
|
@ -321,7 +322,7 @@ macro_rules! create_config {
|
||||||
///
|
///
|
||||||
/// Returns the `Config` to use, and the path of the project file if there was
|
/// Returns the `Config` to use, and the path of the project file if there was
|
||||||
/// one.
|
/// one.
|
||||||
pub fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
|
pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> {
|
||||||
|
|
||||||
/// Try to find a project file in the given directory and its parents.
|
/// 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,
|
/// Returns the path of a the nearest project file if one exists,
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ impl Range {
|
||||||
self.lo > self.hi
|
self.lo > self.hi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn contains(self, other: Range) -> bool {
|
fn contains(self, other: Range) -> bool {
|
||||||
if other.is_empty() {
|
if other.is_empty() {
|
||||||
true
|
true
|
||||||
|
|
@ -128,12 +129,12 @@ fn normalize_ranges(ranges: &mut HashMap<FileName, Vec<Range>>) {
|
||||||
|
|
||||||
impl FileLines {
|
impl FileLines {
|
||||||
/// Creates a `FileLines` that contains all lines in all files.
|
/// Creates a `FileLines` that contains all lines in all files.
|
||||||
pub fn all() -> FileLines {
|
pub(crate) fn all() -> FileLines {
|
||||||
FileLines(None)
|
FileLines(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this `FileLines` contains all lines in all files.
|
/// Returns true if this `FileLines` contains all lines in all files.
|
||||||
pub fn is_all(&self) -> bool {
|
pub(crate) fn is_all(&self) -> bool {
|
||||||
self.0.is_none()
|
self.0.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,22 +167,23 @@ impl FileLines {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if `range` is fully contained in `self`.
|
/// Returns true if `range` is fully contained in `self`.
|
||||||
pub fn contains(&self, range: &LineRange) -> bool {
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn contains(&self, range: &LineRange) -> bool {
|
||||||
self.file_range_matches(range.file_name(), |r| r.contains(Range::from(range)))
|
self.file_range_matches(range.file_name(), |r| r.contains(Range::from(range)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if any lines in `range` are in `self`.
|
/// Returns true if any lines in `range` are in `self`.
|
||||||
pub fn intersects(&self, range: &LineRange) -> bool {
|
pub(crate) fn intersects(&self, range: &LineRange) -> bool {
|
||||||
self.file_range_matches(range.file_name(), |r| r.intersects(Range::from(range)))
|
self.file_range_matches(range.file_name(), |r| r.intersects(Range::from(range)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if `line` from `file_name` is in `self`.
|
/// Returns true if `line` from `file_name` is in `self`.
|
||||||
pub fn contains_line(&self, file_name: &FileName, line: usize) -> bool {
|
pub(crate) fn contains_line(&self, file_name: &FileName, line: usize) -> bool {
|
||||||
self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
|
self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if any of the lines between `lo` and `hi` from `file_name` are in `self`.
|
/// Returns true if any of the lines between `lo` and `hi` from `file_name` are in `self`.
|
||||||
pub fn intersects_range(&self, file_name: &FileName, lo: usize, hi: usize) -> bool {
|
pub(crate) fn intersects_range(&self, file_name: &FileName, lo: usize, hi: usize) -> bool {
|
||||||
self.file_range_matches(file_name, |r| r.intersects(Range::new(lo, hi)))
|
self.file_range_matches(file_name, |r| r.intersects(Range::new(lo, hi)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,7 @@ impl TemplateParser {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// # use rustfmt_nightly::config::license::TemplateParser;
|
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// TemplateParser::parse(
|
/// TemplateParser::parse(
|
||||||
/// r"
|
/// r"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
@ -15,23 +16,22 @@ use std::io::{Error, ErrorKind, Read};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
use regex::Regex;
|
use {FmtError, FmtResult};
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod config_type;
|
|
||||||
#[macro_use]
|
|
||||||
mod options;
|
|
||||||
|
|
||||||
pub mod file_lines;
|
|
||||||
pub mod license;
|
|
||||||
pub mod lists;
|
|
||||||
pub mod summary;
|
|
||||||
|
|
||||||
use config::config_type::ConfigType;
|
use config::config_type::ConfigType;
|
||||||
use config::file_lines::FileLines;
|
use config::file_lines::FileLines;
|
||||||
pub use config::lists::*;
|
pub use config::lists::*;
|
||||||
pub use config::options::*;
|
pub use config::options::*;
|
||||||
use config::summary::Summary;
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod config_type;
|
||||||
|
#[macro_use]
|
||||||
|
pub mod options;
|
||||||
|
|
||||||
|
pub mod file_lines;
|
||||||
|
pub mod license;
|
||||||
|
pub mod lists;
|
||||||
|
pub mod summary;
|
||||||
|
|
||||||
/// This macro defines configuration options used in rustfmt. Each option
|
/// This macro defines configuration options used in rustfmt. Each option
|
||||||
/// is defined as follows:
|
/// is defined as follows:
|
||||||
|
|
@ -151,10 +151,37 @@ create_config! {
|
||||||
"'small' heuristic values";
|
"'small' heuristic values";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
|
pub fn load_config(
|
||||||
///
|
file_path: Option<&Path>,
|
||||||
/// Return the path if a config file exists, empty if no file exists, and Error for IO errors
|
options: Option<&CliOptions>,
|
||||||
pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
|
) -> FmtResult<(Config, Option<PathBuf>)> {
|
||||||
|
let over_ride = match options {
|
||||||
|
Some(opts) => config_path(opts)?,
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = if let Some(over_ride) = over_ride {
|
||||||
|
Config::from_toml_path(over_ride.as_ref())
|
||||||
|
.map(|p| (p, Some(over_ride.to_owned())))
|
||||||
|
.map_err(FmtError::from)
|
||||||
|
} else if let Some(file_path) = file_path {
|
||||||
|
Config::from_resolved_toml_path(file_path).map_err(FmtError::from)
|
||||||
|
} else {
|
||||||
|
Ok((Config::default(), None))
|
||||||
|
};
|
||||||
|
|
||||||
|
result.map(|(mut c, p)| {
|
||||||
|
if let Some(options) = options {
|
||||||
|
options.clone().apply_to(&mut c);
|
||||||
|
}
|
||||||
|
(c, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir`
|
||||||
|
//
|
||||||
|
// Return the path if a config file exists, empty if no file exists, and Error for IO errors
|
||||||
|
fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
|
||||||
const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
|
const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
|
||||||
for config_file_name in &CONFIG_FILE_NAMES {
|
for config_file_name in &CONFIG_FILE_NAMES {
|
||||||
let config_file = dir.join(config_file_name);
|
let config_file = dir.join(config_file_name);
|
||||||
|
|
@ -175,6 +202,30 @@ pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn config_path(options: &CliOptions) -> FmtResult<Option<PathBuf>> {
|
||||||
|
let config_path_not_found = |path: &str| -> FmtResult<Option<PathBuf>> {
|
||||||
|
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.
|
||||||
|
match options.config_path {
|
||||||
|
Some(ref path) if !path.exists() => 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() {
|
||||||
|
Ok(config_file_path)
|
||||||
|
} else {
|
||||||
|
config_path_not_found(path.to_str().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref path => Ok(path.to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::Config;
|
use super::Config;
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,15 @@
|
||||||
use syntax::codemap::FileName;
|
use syntax::codemap::FileName;
|
||||||
|
|
||||||
use config::config_type::ConfigType;
|
use config::config_type::ConfigType;
|
||||||
|
use config::file_lines::FileLines;
|
||||||
use config::lists::*;
|
use config::lists::*;
|
||||||
|
use config::Config;
|
||||||
|
use {FmtError, FmtResult, WRITE_MODE_LIST};
|
||||||
|
|
||||||
|
use getopts::Matches;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// Macro for deriving implementations of Serialize/Deserialize for enums
|
/// Macro for deriving implementations of Serialize/Deserialize for enums
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
@ -301,3 +306,101 @@ impl ::std::str::FromStr for IgnoreList {
|
||||||
Err("IgnoreList is not parsable")
|
Err("IgnoreList is not parsable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parsed command line options.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct CliOptions {
|
||||||
|
skip_children: Option<bool>,
|
||||||
|
verbose: bool,
|
||||||
|
verbose_diff: bool,
|
||||||
|
pub(super) config_path: Option<PathBuf>,
|
||||||
|
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 {
|
||||||
|
pub fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
|
||||||
|
let mut options = CliOptions::default();
|
||||||
|
options.verbose = matches.opt_present("verbose");
|
||||||
|
options.verbose_diff = matches.opt_present("verbose-diff");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.config_path = matches.opt_str("config-path").map(PathBuf::from);
|
||||||
|
|
||||||
|
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: {}, expected one of {}",
|
||||||
|
write_mode, WRITE_MODE_LIST
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_to(self, config: &mut Config) {
|
||||||
|
config.set().verbose(self.verbose);
|
||||||
|
config.set().verbose_diff(self.verbose_diff);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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)
|
||||||
|
}
|
||||||
|
_ => eprintln!("Warning: Not a file '{}'", f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,16 @@ pub struct Summary {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Summary {
|
impl Summary {
|
||||||
pub fn mark_parse_time(&mut self) {
|
pub(crate) fn mark_parse_time(&mut self) {
|
||||||
self.timer = self.timer.done_parsing();
|
self.timer = self.timer.done_parsing();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_format_time(&mut self) {
|
pub(crate) fn mark_format_time(&mut self) {
|
||||||
self.timer = self.timer.done_formatting();
|
self.timer = self.timer.done_formatting();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the time it took to parse the source files in nanoseconds.
|
/// Returns the time it took to parse the source files in nanoseconds.
|
||||||
pub fn get_parse_time(&self) -> Option<Duration> {
|
pub(crate) fn get_parse_time(&self) -> Option<Duration> {
|
||||||
match self.timer {
|
match self.timer {
|
||||||
Timer::DoneParsing(init, parse_time) | Timer::DoneFormatting(init, parse_time, _) => {
|
Timer::DoneParsing(init, parse_time) | Timer::DoneFormatting(init, parse_time, _) => {
|
||||||
// This should never underflow since `Instant::now()` guarantees monotonicity.
|
// This should never underflow since `Instant::now()` guarantees monotonicity.
|
||||||
|
|
@ -52,7 +52,7 @@ impl Summary {
|
||||||
|
|
||||||
/// Returns the time it took to go from the parsed AST to the formatted output. Parsing time is
|
/// Returns the time it took to go from the parsed AST to the formatted output. Parsing time is
|
||||||
/// not included.
|
/// not included.
|
||||||
pub fn get_format_time(&self) -> Option<Duration> {
|
pub(crate) fn get_format_time(&self) -> Option<Duration> {
|
||||||
match self.timer {
|
match self.timer {
|
||||||
Timer::DoneFormatting(_init, parse_time, format_time) => {
|
Timer::DoneFormatting(_init, parse_time, format_time) => {
|
||||||
Some(format_time.duration_since(parse_time))
|
Some(format_time.duration_since(parse_time))
|
||||||
|
|
@ -77,15 +77,15 @@ impl Summary {
|
||||||
self.has_operational_errors = true;
|
self.has_operational_errors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_parsing_error(&mut self) {
|
pub(crate) fn add_parsing_error(&mut self) {
|
||||||
self.has_parsing_errors = true;
|
self.has_parsing_errors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_formatting_error(&mut self) {
|
pub(crate) fn add_formatting_error(&mut self) {
|
||||||
self.has_formatting_errors = true;
|
self.has_formatting_errors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_diff(&mut self) {
|
pub(crate) fn add_diff(&mut self) {
|
||||||
self.has_diff = true;
|
self.has_diff = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,12 @@ use std::fs::{self, File};
|
||||||
use std::io::{self, BufWriter, Read, Write};
|
use std::io::{self, BufWriter, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use checkstyle::{output_checkstyle_file, output_footer, output_header};
|
use checkstyle::output_checkstyle_file;
|
||||||
use config::{Config, NewlineStyle, WriteMode};
|
use config::{Config, NewlineStyle, WriteMode};
|
||||||
use rustfmt_diff::{make_diff, output_modified, print_diff, Mismatch};
|
use rustfmt_diff::{make_diff, output_modified, print_diff, Mismatch};
|
||||||
use syntax::codemap::FileName;
|
use syntax::codemap::FileName;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
use FileRecord;
|
use FileRecord;
|
||||||
|
|
||||||
// Append a newline to the end of each file.
|
// Append a newline to the end of each file.
|
||||||
|
|
@ -26,7 +27,8 @@ pub fn append_newline(s: &mut String) {
|
||||||
s.push_str("\n");
|
s.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_all_files<T>(
|
#[cfg(test)]
|
||||||
|
pub(crate) fn write_all_files<T>(
|
||||||
file_map: &[FileRecord],
|
file_map: &[FileRecord],
|
||||||
out: &mut T,
|
out: &mut T,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
|
|
@ -34,11 +36,15 @@ pub fn write_all_files<T>(
|
||||||
where
|
where
|
||||||
T: Write,
|
T: Write,
|
||||||
{
|
{
|
||||||
output_header(out, config.write_mode()).ok();
|
if config.write_mode() == WriteMode::Checkstyle {
|
||||||
|
::checkstyle::output_header(out)?;
|
||||||
|
}
|
||||||
for &(ref filename, ref text) in file_map {
|
for &(ref filename, ref text) in file_map {
|
||||||
write_file(text, filename, out, config)?;
|
write_file(text, filename, out, config)?;
|
||||||
}
|
}
|
||||||
output_footer(out, config.write_mode()).ok();
|
if config.write_mode() == WriteMode::Checkstyle {
|
||||||
|
::checkstyle::output_footer(out)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use getopts::{Matches, Options};
|
use getopts::{Matches, Options};
|
||||||
|
|
||||||
use rustfmt::{config, run, Input};
|
use rustfmt::{format_and_emit_report, load_config, Input};
|
||||||
|
|
||||||
fn prune_files(files: Vec<&str>) -> Vec<&str> {
|
fn prune_files(files: Vec<&str>) -> Vec<&str> {
|
||||||
let prefixes: Vec<_> = files
|
let prefixes: Vec<_> = files
|
||||||
|
|
@ -68,12 +68,11 @@ fn get_files(input: &str) -> Vec<&str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_files(files: &[&str]) -> i32 {
|
fn fmt_files(files: &[&str]) -> i32 {
|
||||||
let (config, _) = config::Config::from_resolved_toml_path(Path::new("."))
|
let (config, _) = load_config(Some(Path::new(".")), None).expect("couldn't load config");
|
||||||
.unwrap_or_else(|_| (config::Config::default(), None));
|
|
||||||
|
|
||||||
let mut exit_code = 0;
|
let mut exit_code = 0;
|
||||||
for file in files {
|
for file in files {
|
||||||
let summary = run(Input::File(PathBuf::from(file)), &config);
|
let summary = format_and_emit_report(Input::File(PathBuf::from(file)), &config).unwrap();
|
||||||
if !summary.has_no_errors() {
|
if !summary.has_no_errors() {
|
||||||
exit_code = 1;
|
exit_code = 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
src/lib.rs
75
src/lib.rs
|
|
@ -19,7 +19,11 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate derive_new;
|
extern crate derive_new;
|
||||||
extern crate diff;
|
extern crate diff;
|
||||||
|
extern crate getopts;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
@ -33,6 +37,7 @@ extern crate toml;
|
||||||
extern crate unicode_segmentation;
|
extern crate unicode_segmentation;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{self, stdout, BufRead, Write};
|
use std::io::{self, stdout, BufRead, Write};
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
|
|
@ -53,21 +58,28 @@ use shape::Indent;
|
||||||
use utils::use_colored_tty;
|
use utils::use_colored_tty;
|
||||||
use visitor::{FmtVisitor, SnippetProvider};
|
use visitor::{FmtVisitor, SnippetProvider};
|
||||||
|
|
||||||
|
pub use config::options::CliOptions;
|
||||||
pub use config::summary::Summary;
|
pub use config::summary::Summary;
|
||||||
pub use config::Config;
|
pub use config::{file_lines, load_config, Config, WriteMode};
|
||||||
|
|
||||||
|
pub type FmtError = Box<error::Error + Send + Sync>;
|
||||||
|
pub type FmtResult<T> = std::result::Result<T, FmtError>;
|
||||||
|
|
||||||
|
pub const WRITE_MODE_LIST: &str =
|
||||||
|
"[replace|overwrite|display|plain|diff|coverage|checkstyle|check]";
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
mod attr;
|
mod attr;
|
||||||
mod chains;
|
mod chains;
|
||||||
pub mod checkstyle;
|
pub(crate) mod checkstyle;
|
||||||
mod closures;
|
mod closures;
|
||||||
pub mod codemap;
|
pub(crate) mod codemap;
|
||||||
mod comment;
|
mod comment;
|
||||||
pub mod config;
|
pub(crate) mod config;
|
||||||
mod expr;
|
mod expr;
|
||||||
pub mod filemap;
|
pub(crate) mod filemap;
|
||||||
mod imports;
|
mod imports;
|
||||||
mod issues;
|
mod issues;
|
||||||
mod items;
|
mod items;
|
||||||
|
|
@ -75,25 +87,27 @@ mod lists;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod matches;
|
mod matches;
|
||||||
mod missed_spans;
|
mod missed_spans;
|
||||||
pub mod modules;
|
pub(crate) mod modules;
|
||||||
mod overflow;
|
mod overflow;
|
||||||
mod patterns;
|
mod patterns;
|
||||||
mod reorder;
|
mod reorder;
|
||||||
mod rewrite;
|
mod rewrite;
|
||||||
pub mod rustfmt_diff;
|
pub(crate) mod rustfmt_diff;
|
||||||
mod shape;
|
mod shape;
|
||||||
mod spanned;
|
mod spanned;
|
||||||
mod string;
|
mod string;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
mod types;
|
mod types;
|
||||||
mod vertical;
|
mod vertical;
|
||||||
pub mod visitor;
|
pub(crate) mod visitor;
|
||||||
|
|
||||||
const STDIN: &str = "<stdin>";
|
const STDIN: &str = "<stdin>";
|
||||||
|
|
||||||
// A map of the files of a crate, with their new content
|
// A map of the files of a crate, with their new content
|
||||||
pub type FileMap = Vec<FileRecord>;
|
pub(crate) type FileMap = Vec<FileRecord>;
|
||||||
|
|
||||||
pub type FileRecord = (FileName, String);
|
pub(crate) type FileRecord = (FileName, String);
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
|
|
@ -123,7 +137,7 @@ impl fmt::Display for ErrorKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formatting errors that are identified *after* rustfmt has run.
|
// Formatting errors that are identified *after* rustfmt has run.
|
||||||
pub struct FormattingError {
|
struct FormattingError {
|
||||||
line: usize,
|
line: usize,
|
||||||
kind: ErrorKind,
|
kind: ErrorKind,
|
||||||
is_comment: bool,
|
is_comment: bool,
|
||||||
|
|
@ -150,7 +164,7 @@ impl FormattingError {
|
||||||
}
|
}
|
||||||
|
|
||||||
// (space, target)
|
// (space, target)
|
||||||
pub fn format_len(&self) -> (usize, usize) {
|
fn format_len(&self) -> (usize, usize) {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ErrorKind::LineOverflow(found, max) => (max, found - max),
|
ErrorKind::LineOverflow(found, max) => (max, found - max),
|
||||||
ErrorKind::TrailingWhitespace => {
|
ErrorKind::TrailingWhitespace => {
|
||||||
|
|
@ -180,18 +194,18 @@ impl FormatReport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn warning_count(&self) -> usize {
|
fn warning_count(&self) -> usize {
|
||||||
self.file_error_map
|
self.file_error_map
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, errors)| errors.len())
|
.map(|(_, errors)| errors.len())
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_warnings(&self) -> bool {
|
fn has_warnings(&self) -> bool {
|
||||||
self.warning_count() > 0
|
self.warning_count() > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_warnings_fancy(
|
fn print_warnings_fancy(
|
||||||
&self,
|
&self,
|
||||||
mut t: Box<term::Terminal<Output = io::Stderr>>,
|
mut t: Box<term::Terminal<Output = io::Stderr>>,
|
||||||
) -> Result<(), term::Error> {
|
) -> Result<(), term::Error> {
|
||||||
|
|
@ -881,7 +895,10 @@ pub enum Input {
|
||||||
Text(String),
|
Text(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(input: Input, config: &Config) -> Summary {
|
pub fn format_and_emit_report(input: Input, config: &Config) -> FmtResult<Summary> {
|
||||||
|
if !config.version_meets_requirement() {
|
||||||
|
return Err(FmtError::from("Version mismatch"));
|
||||||
|
}
|
||||||
let out = &mut stdout();
|
let out = &mut stdout();
|
||||||
match format_input(input, config, Some(out)) {
|
match format_input(input, config, Some(out)) {
|
||||||
Ok((summary, _, report)) => {
|
Ok((summary, _, report)) => {
|
||||||
|
|
@ -896,22 +913,38 @@ pub fn run(input: Input, config: &Config) -> Summary {
|
||||||
Err(..) => panic!("Unable to write to stderr: {}", report),
|
Err(..) => panic!("Unable to write to stderr: {}", report),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => msg!("{}", report),
|
_ => eprintln!("{}", report),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
summary
|
Ok(summary)
|
||||||
}
|
}
|
||||||
Err((msg, mut summary)) => {
|
Err((msg, mut summary)) => {
|
||||||
msg!("Error writing files: {}", msg);
|
eprintln!("Error writing files: {}", msg);
|
||||||
summary.add_operational_error();
|
summary.add_operational_error();
|
||||||
summary
|
Ok(summary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn emit_pre_matter(config: &Config) -> FmtResult<()> {
|
||||||
|
if config.write_mode() == WriteMode::Checkstyle {
|
||||||
|
let mut out = &mut stdout();
|
||||||
|
checkstyle::output_header(&mut out)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_post_matter(config: &Config) -> FmtResult<()> {
|
||||||
|
if config.write_mode() == WriteMode::Checkstyle {
|
||||||
|
let mut out = &mut stdout();
|
||||||
|
checkstyle::output_footer(&mut out)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod unit_tests {
|
||||||
use super::{format_code_block, format_snippet, Config};
|
use super::{format_code_block, format_snippet, Config};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -197,14 +197,6 @@ impl Shape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape {
|
|
||||||
Shape {
|
|
||||||
width,
|
|
||||||
indent,
|
|
||||||
offset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visual_indent(&self, extra_width: usize) -> Shape {
|
pub fn visual_indent(&self, extra_width: usize) -> Shape {
|
||||||
let alignment = self.offset + extra_width;
|
let alignment = self.offset + extra_width;
|
||||||
Shape {
|
Shape {
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,6 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
extern crate assert_cli;
|
extern crate assert_cli;
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
extern crate regex;
|
|
||||||
extern crate rustfmt_nightly as rustfmt;
|
|
||||||
extern crate term;
|
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
@ -24,11 +17,11 @@ use std::iter::{Enumerate, Peekable};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
|
|
||||||
use rustfmt::config::summary::Summary;
|
use config::summary::Summary;
|
||||||
use rustfmt::config::{Color, Config, ReportTactic};
|
use config::{Color, Config, ReportTactic};
|
||||||
use rustfmt::filemap::write_system_newlines;
|
use filemap::write_system_newlines;
|
||||||
use rustfmt::rustfmt_diff::*;
|
use rustfmt_diff::*;
|
||||||
use rustfmt::*;
|
use *;
|
||||||
|
|
||||||
const DIFF_CONTEXT_SIZE: usize = 3;
|
const DIFF_CONTEXT_SIZE: usize = 3;
|
||||||
const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md";
|
const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md";
|
||||||
|
|
@ -255,15 +255,6 @@ pub fn count_newlines(input: &str) -> usize {
|
||||||
input.as_bytes().iter().filter(|&&c| c == b'\n').count()
|
input.as_bytes().iter().filter(|&&c| c == b'\n').count()
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! msg {
|
|
||||||
($($arg:tt)*) => (
|
|
||||||
match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
|
|
||||||
Ok(_) => {},
|
|
||||||
Err(x) => panic!("Unable to write to stderr: {}", x),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// For format_missing and last_pos, need to use the source callsite (if applicable).
|
// For format_missing and last_pos, need to use the source callsite (if applicable).
|
||||||
// Required as generated code spans aren't guaranteed to follow on from the last span.
|
// Required as generated code spans aren't guaranteed to follow on from the last span.
|
||||||
macro_rules! source {
|
macro_rules! source {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue