Refactor to make a sensible public API

0.5 - lots of breaking changes

cc #2639
This commit is contained in:
Nick Cameron 2018-04-20 21:08:20 +12:00
parent 6a31741eaa
commit ca610d35b3
16 changed files with 315 additions and 296 deletions

2
Cargo.lock generated
View file

@ -451,7 +451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustfmt-nightly"
version = "0.4.2"
version = "0.5.0"
dependencies = [
"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)",

View file

@ -1,7 +1,7 @@
[package]
name = "rustfmt-nightly"
version = "0.4.2"
version = "0.5.0"
authors = ["Nicholas Cameron <ncameron@mozilla.com>", "The Rustfmt developers"]
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang-nursery/rustfmt"
@ -49,8 +49,8 @@ cargo_metadata = "0.5.1"
rustc-ap-syntax = "103.0.0"
[dev-dependencies]
lazy_static = "1.0.0"
assert_cli = "0.5"
lazy_static = "1.0.0"
[target.'cfg(unix)'.dependencies]
libc = "0.2.11"

View file

@ -14,23 +14,16 @@ extern crate env_logger;
extern crate getopts;
extern crate rustfmt_nightly as rustfmt;
use std::env;
use std::fs::File;
use std::io::{self, stdout, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{env, error};
use std::path::PathBuf;
use getopts::{Matches, Options};
use rustfmt::checkstyle;
use rustfmt::config::file_lines::FileLines;
use rustfmt::config::{get_toml_path, Color, Config, WriteMode};
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]";
use rustfmt::{emit_post_matter, emit_pre_matter, load_config, CliOptions, Config, FmtResult,
WriteMode, WRITE_MODE_LIST};
use rustfmt::{format_and_emit_report, FileName, Input, Summary};
fn main() {
env_logger::init();
@ -66,7 +59,6 @@ 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.
@ -82,105 +74,9 @@ enum Operation {
/// 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,
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 {
let mut opts = Options::new();
@ -280,10 +176,10 @@ fn execute(opts: &Options) -> FmtResult<(WriteMode, Summary)> {
}
Ok((WriteMode::None, Summary::default()))
}
Operation::Stdin { input, config_path } => {
Operation::Stdin { input } => {
// try to read config from local directory
let (mut config, _) =
match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;
let options = CliOptions::from_matches(&matches)?;
let (mut config, _) = load_config(None, Some(&options))?;
// write_mode is always Plain for Stdin.
config.set().write_mode(WriteMode::Plain);
@ -300,57 +196,40 @@ fn execute(opts: &Options) -> FmtResult<(WriteMode, Summary)> {
}
let mut error_summary = Summary::default();
if config.version_meets_requirement(&mut error_summary) {
let mut out = &mut stdout();
checkstyle::output_header(&mut out, config.write_mode())?;
error_summary.add(run(Input::Text(input), &config));
checkstyle::output_footer(&mut out, config.write_mode())?;
emit_pre_matter(&config)?;
match format_and_emit_report(Input::Text(input), &config) {
Ok(summary) => error_summary.add(summary),
Err(_) => error_summary.add_operational_error(),
}
emit_post_matter(&config)?;
Ok((WriteMode::Plain, error_summary))
}
Operation::Format {
files,
config_path,
minimal_config_path,
} => {
let options = CliOptions::from_matches(&matches)?;
format(files, config_path, minimal_config_path, options)
format(files, minimal_config_path, options)
}
}
}
fn format(
files: Vec<PathBuf>,
config_path: Option<PathBuf>,
minimal_config_path: Option<String>,
options: CliOptions,
) -> FmtResult<(WriteMode, Summary)> {
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),
}
}
options.verify_file_lines(&files);
let (config, config_path) = load_config(None, Some(&options))?;
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())?;
};
if options.verbose {
if config.verbose() {
if let Some(path) = config_path.as_ref() {
println!("Using rustfmt config file {}", path.display());
}
}
let write_mode = config.write_mode();
let mut out = &mut stdout();
checkstyle::output_header(&mut out, write_mode)?;
emit_pre_matter(&config)?;
let mut error_summary = Summary::default();
for file in files {
@ -362,11 +241,11 @@ fn format(
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() {
let local_config = if config_path.is_none() {
let (local_config, config_path) =
load_config(Some(file.parent().unwrap()), Some(&options))?;
if local_config.verbose() {
if let Some(path) = config_path {
println!(
"Using rustfmt config file {} for {}",
path.display(),
@ -374,18 +253,21 @@ fn format(
);
}
}
config = config_tmp;
}
local_config
} else {
config.clone()
};
if !config.version_meets_requirement(&mut error_summary) {
break;
match format_and_emit_report(Input::File(file), &local_config) {
Ok(summary) => error_summary.add(summary),
Err(_) => {
error_summary.add_operational_error();
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
// that were used during formatting as TOML.
@ -395,7 +277,7 @@ fn format(
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) {
@ -451,28 +333,6 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
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");
@ -481,10 +341,7 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
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
@ -500,7 +357,6 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
Ok(Operation::Format {
files,
config_path,
minimal_config_path,
})
}

View file

@ -11,33 +11,26 @@
use std::io::{self, Write};
use std::path::Path;
use config::WriteMode;
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
T: Write,
{
if mode == WriteMode::Checkstyle {
let mut xml_heading = String::new();
xml_heading.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
xml_heading.push_str("\n");
xml_heading.push_str("<checkstyle version=\"4.3\">");
write!(out, "{}", xml_heading)?;
}
Ok(())
let mut xml_heading = String::new();
xml_heading.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
xml_heading.push_str("\n");
xml_heading.push_str("<checkstyle version=\"4.3\">");
write!(out, "{}", xml_heading)
}
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
T: Write,
{
if mode == WriteMode::Checkstyle {
let mut xml_tail = String::new();
xml_tail.push_str("</checkstyle>\n");
write!(out, "{}", xml_tail)?;
}
Ok(())
let mut xml_tail = String::new();
xml_tail.push_str("</checkstyle>\n");
write!(out, "{}", xml_tail)
}
pub fn output_checkstyle_file<T>(

View file

@ -80,6 +80,7 @@ macro_rules! is_nightly_channel {
macro_rules! create_config {
($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
#[cfg(test)]
use std::collections::HashSet;
use std::io::Write;
@ -150,7 +151,7 @@ macro_rules! create_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() {
let version = env!("CARGO_PKG_VERSION");
let required_version = self.required_version();
@ -160,7 +161,6 @@ macro_rules! create_config {
version,
required_version,
);
error_summary.add_formatting_error();
return false;
}
}
@ -207,7 +207,8 @@ macro_rules! create_config {
}
/// 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();
$(
hash_set.insert(stringify!($i).to_owned());
@ -215,7 +216,7 @@ macro_rules! create_config {
hash_set
}
pub fn is_valid_name(name: &str) -> bool {
pub(crate) fn is_valid_name(name: &str) -> bool {
match name {
$(
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 =
toml.parse().map_err(|e| format!("Could not parse TOML: {}", e))?;
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
/// 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 toml = String::new();
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
/// 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.
/// Returns the path of a the nearest project file if one exists,

View file

@ -54,6 +54,7 @@ impl Range {
self.lo > self.hi
}
#[allow(dead_code)]
fn contains(self, other: Range) -> bool {
if other.is_empty() {
true
@ -128,12 +129,12 @@ fn normalize_ranges(ranges: &mut HashMap<FileName, Vec<Range>>) {
impl FileLines {
/// Creates a `FileLines` that contains all lines in all files.
pub fn all() -> FileLines {
pub(crate) fn all() -> FileLines {
FileLines(None)
}
/// 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()
}
@ -166,22 +167,23 @@ impl FileLines {
}
/// 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)))
}
/// 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)))
}
/// 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)
}
/// 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)))
}
}

View file

@ -82,8 +82,7 @@ impl TemplateParser {
///
/// # Examples
///
/// ```
/// # use rustfmt_nightly::config::license::TemplateParser;
/// ```ignore
/// assert_eq!(
/// TemplateParser::parse(
/// r"

View file

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use regex::Regex;
use std::cell::Cell;
use std::default::Default;
use std::fs::File;
@ -15,23 +16,22 @@ use std::io::{Error, ErrorKind, Read};
use std::path::{Path, PathBuf};
use std::{env, fs};
use regex::Regex;
#[macro_use]
mod config_type;
#[macro_use]
mod options;
pub mod file_lines;
pub mod license;
pub mod lists;
pub mod summary;
use {FmtError, FmtResult};
use config::config_type::ConfigType;
use config::file_lines::FileLines;
pub use config::lists::*;
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
/// is defined as follows:
@ -151,10 +151,37 @@ create_config! {
"'small' heuristic values";
}
/// 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
pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
pub fn load_config(
file_path: Option<&Path>,
options: Option<&CliOptions>,
) -> 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"];
for config_file_name in &CONFIG_FILE_NAMES {
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)
}
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)]
mod test {
use super::Config;

View file

@ -11,10 +11,15 @@
use syntax::codemap::FileName;
use config::config_type::ConfigType;
use config::file_lines::FileLines;
use config::lists::*;
use config::Config;
use {FmtError, FmtResult, WRITE_MODE_LIST};
use getopts::Matches;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::str::FromStr;
/// Macro for deriving implementations of Serialize/Deserialize for enums
#[macro_export]
@ -301,3 +306,101 @@ impl ::std::str::FromStr for IgnoreList {
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),
}
}
}
}

View file

@ -31,16 +31,16 @@ pub struct Summary {
}
impl Summary {
pub fn mark_parse_time(&mut self) {
pub(crate) fn mark_parse_time(&mut self) {
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();
}
/// 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 {
Timer::DoneParsing(init, parse_time) | Timer::DoneFormatting(init, parse_time, _) => {
// 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
/// not included.
pub fn get_format_time(&self) -> Option<Duration> {
pub(crate) fn get_format_time(&self) -> Option<Duration> {
match self.timer {
Timer::DoneFormatting(_init, parse_time, format_time) => {
Some(format_time.duration_since(parse_time))
@ -77,15 +77,15 @@ impl Summary {
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;
}
pub fn add_formatting_error(&mut self) {
pub(crate) fn add_formatting_error(&mut self) {
self.has_formatting_errors = true;
}
pub fn add_diff(&mut self) {
pub(crate) fn add_diff(&mut self) {
self.has_diff = true;
}

View file

@ -14,11 +14,12 @@ use std::fs::{self, File};
use std::io::{self, BufWriter, Read, Write};
use std::path::Path;
use checkstyle::{output_checkstyle_file, output_footer, output_header};
use checkstyle::output_checkstyle_file;
use config::{Config, NewlineStyle, WriteMode};
use rustfmt_diff::{make_diff, output_modified, print_diff, Mismatch};
use syntax::codemap::FileName;
#[cfg(test)]
use FileRecord;
// Append a newline to the end of each file.
@ -26,7 +27,8 @@ pub fn append_newline(s: &mut String) {
s.push_str("\n");
}
pub fn write_all_files<T>(
#[cfg(test)]
pub(crate) fn write_all_files<T>(
file_map: &[FileRecord],
out: &mut T,
config: &Config,
@ -34,11 +36,15 @@ pub fn write_all_files<T>(
where
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 {
write_file(text, filename, out, config)?;
}
output_footer(out, config.write_mode()).ok();
if config.write_mode() == WriteMode::Checkstyle {
::checkstyle::output_footer(out)?;
}
Ok(())
}

View file

@ -21,7 +21,7 @@ use std::str::FromStr;
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> {
let prefixes: Vec<_> = files
@ -68,12 +68,11 @@ fn get_files(input: &str) -> Vec<&str> {
}
fn fmt_files(files: &[&str]) -> i32 {
let (config, _) = config::Config::from_resolved_toml_path(Path::new("."))
.unwrap_or_else(|_| (config::Config::default(), None));
let (config, _) = load_config(Some(Path::new(".")), None).expect("couldn't load config");
let mut exit_code = 0;
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() {
exit_code = 1;
}

View file

@ -19,7 +19,11 @@
#[macro_use]
extern crate derive_new;
extern crate diff;
extern crate getopts;
extern crate itertools;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate regex;
@ -33,6 +37,7 @@ extern crate toml;
extern crate unicode_segmentation;
use std::collections::HashMap;
use std::error;
use std::fmt;
use std::io::{self, stdout, BufRead, Write};
use std::panic::{catch_unwind, AssertUnwindSafe};
@ -53,21 +58,28 @@ use shape::Indent;
use utils::use_colored_tty;
use visitor::{FmtVisitor, SnippetProvider};
pub use config::options::CliOptions;
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]
mod utils;
mod attr;
mod chains;
pub mod checkstyle;
pub(crate) mod checkstyle;
mod closures;
pub mod codemap;
pub(crate) mod codemap;
mod comment;
pub mod config;
pub(crate) mod config;
mod expr;
pub mod filemap;
pub(crate) mod filemap;
mod imports;
mod issues;
mod items;
@ -75,25 +87,27 @@ mod lists;
mod macros;
mod matches;
mod missed_spans;
pub mod modules;
pub(crate) mod modules;
mod overflow;
mod patterns;
mod reorder;
mod rewrite;
pub mod rustfmt_diff;
pub(crate) mod rustfmt_diff;
mod shape;
mod spanned;
mod string;
#[cfg(test)]
mod test;
mod types;
mod vertical;
pub mod visitor;
pub(crate) mod visitor;
const STDIN: &str = "<stdin>";
// 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)]
pub enum ErrorKind {
@ -123,7 +137,7 @@ impl fmt::Display for ErrorKind {
}
// Formatting errors that are identified *after* rustfmt has run.
pub struct FormattingError {
struct FormattingError {
line: usize,
kind: ErrorKind,
is_comment: bool,
@ -150,7 +164,7 @@ impl FormattingError {
}
// (space, target)
pub fn format_len(&self) -> (usize, usize) {
fn format_len(&self) -> (usize, usize) {
match self.kind {
ErrorKind::LineOverflow(found, max) => (max, found - max),
ErrorKind::TrailingWhitespace => {
@ -180,18 +194,18 @@ impl FormatReport {
}
}
pub fn warning_count(&self) -> usize {
fn warning_count(&self) -> usize {
self.file_error_map
.iter()
.map(|(_, errors)| errors.len())
.sum()
}
pub fn has_warnings(&self) -> bool {
fn has_warnings(&self) -> bool {
self.warning_count() > 0
}
pub fn print_warnings_fancy(
fn print_warnings_fancy(
&self,
mut t: Box<term::Terminal<Output = io::Stderr>>,
) -> Result<(), term::Error> {
@ -881,7 +895,10 @@ pub enum Input {
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();
match format_input(input, config, Some(out)) {
Ok((summary, _, report)) => {
@ -896,22 +913,38 @@ pub fn run(input: Input, config: &Config) -> Summary {
Err(..) => panic!("Unable to write to stderr: {}", report),
}
}
_ => msg!("{}", report),
_ => eprintln!("{}", report),
}
}
summary
Ok(summary)
}
Err((msg, mut summary)) => {
msg!("Error writing files: {}", msg);
eprintln!("Error writing files: {}", msg);
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)]
mod test {
mod unit_tests {
use super::{format_code_block, format_snippet, Config};
#[test]

View file

@ -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 {
let alignment = self.offset + extra_width;
Shape {

View file

@ -9,13 +9,6 @@
// except according to those terms.
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::fs;
@ -24,11 +17,11 @@ use std::iter::{Enumerate, Peekable};
use std::path::{Path, PathBuf};
use std::str::Chars;
use rustfmt::config::summary::Summary;
use rustfmt::config::{Color, Config, ReportTactic};
use rustfmt::filemap::write_system_newlines;
use rustfmt::rustfmt_diff::*;
use rustfmt::*;
use config::summary::Summary;
use config::{Color, Config, ReportTactic};
use filemap::write_system_newlines;
use rustfmt_diff::*;
use *;
const DIFF_CONTEXT_SIZE: usize = 3;
const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md";

View file

@ -255,15 +255,6 @@ pub fn count_newlines(input: &str) -> usize {
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).
// Required as generated code spans aren't guaranteed to follow on from the last span.
macro_rules! source {