2019-01-23 09:33:51 -05:00
|
|
|
// 2015-edition macros.
|
|
|
|
#[macro_use]
|
|
|
|
extern crate clap;
|
|
|
|
|
2019-07-19 23:45:41 -04:00
|
|
|
use std::io::Write;
|
|
|
|
|
2019-01-22 17:01:33 -05:00
|
|
|
use structopt::StructOpt;
|
|
|
|
|
2019-06-14 08:43:21 -04:00
|
|
|
arg_enum! {
|
2019-01-23 09:33:51 -05:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
enum Format {
|
|
|
|
Silent,
|
|
|
|
Brief,
|
|
|
|
Long,
|
|
|
|
Json,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Format {
|
2019-07-03 21:22:36 -04:00
|
|
|
fn report(self) -> typos::report::Report {
|
2019-01-23 09:33:51 -05:00
|
|
|
match self {
|
2019-07-03 21:22:36 -04:00
|
|
|
Format::Silent => typos::report::print_silent,
|
|
|
|
Format::Brief => typos::report::print_brief,
|
|
|
|
Format::Long => typos::report::print_long,
|
|
|
|
Format::Json => typos::report::print_json,
|
2019-01-23 09:33:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Format {
|
|
|
|
fn default() -> Self {
|
|
|
|
Format::Long
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-22 17:01:33 -05:00
|
|
|
#[derive(Debug, StructOpt)]
|
2019-07-10 09:21:02 -04:00
|
|
|
#[structopt(rename_all = "kebab-case")]
|
2019-01-22 17:01:33 -05:00
|
|
|
struct Options {
|
2019-01-23 09:34:05 -05:00
|
|
|
#[structopt(parse(from_os_str), default_value = ".")]
|
2019-01-22 17:01:33 -05:00
|
|
|
/// Paths to check
|
|
|
|
path: Vec<std::path::PathBuf>,
|
|
|
|
|
2019-07-18 22:20:45 -04:00
|
|
|
#[structopt(long, raw(overrides_with = r#""check-filenames""#))]
|
|
|
|
/// Skip verifying spelling in file names.
|
|
|
|
no_check_filenames: bool,
|
|
|
|
#[structopt(
|
|
|
|
long,
|
|
|
|
raw(overrides_with = r#""no-check-filenames""#),
|
|
|
|
raw(hidden = "true")
|
|
|
|
)]
|
|
|
|
check_filenames: bool,
|
|
|
|
|
2019-07-19 09:24:15 -04:00
|
|
|
#[structopt(long, raw(overrides_with = r#""check-files""#))]
|
|
|
|
/// Skip verifying spelling in filess.
|
|
|
|
no_check_files: bool,
|
|
|
|
#[structopt(
|
|
|
|
long,
|
|
|
|
raw(overrides_with = r#""no-check-files""#),
|
|
|
|
raw(hidden = "true")
|
|
|
|
)]
|
|
|
|
check_files: bool,
|
|
|
|
|
2019-07-13 21:24:27 -04:00
|
|
|
#[structopt(long, raw(overrides_with = r#""hex""#))]
|
|
|
|
/// Don't try to detect that an identifier looks like hex
|
|
|
|
no_hex: bool,
|
|
|
|
#[structopt(long, raw(overrides_with = r#""no-hex""#), raw(hidden = "true"))]
|
|
|
|
hex: bool,
|
|
|
|
|
2019-06-14 08:43:21 -04:00
|
|
|
#[structopt(
|
|
|
|
long = "format",
|
|
|
|
raw(possible_values = "&Format::variants()", case_insensitive = "true"),
|
|
|
|
default_value = "long"
|
|
|
|
)]
|
2019-01-23 09:33:51 -05:00
|
|
|
pub format: Format,
|
|
|
|
|
2019-06-14 08:43:21 -04:00
|
|
|
#[structopt(short = "j", long = "threads", default_value = "0")]
|
2019-01-22 17:01:33 -05:00
|
|
|
/// The approximate number of threads to use.
|
|
|
|
threads: usize,
|
2019-07-10 09:21:02 -04:00
|
|
|
|
2019-07-13 22:14:06 -04:00
|
|
|
#[structopt(long, raw(overrides_with = r#""no-binary""#))]
|
|
|
|
/// Search binary files.
|
|
|
|
binary: bool,
|
|
|
|
#[structopt(long, raw(overrides_with = r#""binary""#), raw(hidden = "true"))]
|
|
|
|
no_binary: bool,
|
|
|
|
|
2019-07-10 09:21:02 -04:00
|
|
|
#[structopt(long, raw(overrides_with = r#""no-hidden""#))]
|
|
|
|
/// Search hidden files and directories.
|
|
|
|
hidden: bool,
|
|
|
|
#[structopt(long, raw(overrides_with = r#""hidden""#), raw(hidden = "true"))]
|
|
|
|
no_hidden: bool,
|
2019-07-10 22:12:14 -04:00
|
|
|
|
2019-07-11 23:56:27 -04:00
|
|
|
#[structopt(long, raw(overrides_with = r#""ignore""#))]
|
|
|
|
/// Don't respect ignore files.
|
|
|
|
no_ignore: bool,
|
|
|
|
#[structopt(long, raw(overrides_with = r#""no-ignore""#), raw(hidden = "true"))]
|
|
|
|
ignore: bool,
|
|
|
|
|
2019-07-10 22:12:14 -04:00
|
|
|
#[structopt(long, raw(overrides_with = r#""ignore-dot""#))]
|
|
|
|
/// Don't respect .ignore files.
|
|
|
|
no_ignore_dot: bool,
|
|
|
|
#[structopt(long, raw(overrides_with = r#""no-ignore-dot""#), raw(hidden = "true"))]
|
|
|
|
ignore_dot: bool,
|
2019-07-12 23:36:32 -04:00
|
|
|
|
|
|
|
#[structopt(long, raw(overrides_with = r#""ignore-global""#))]
|
|
|
|
/// Don't respect global ignore files.
|
|
|
|
no_ignore_global: bool,
|
|
|
|
#[structopt(
|
|
|
|
long,
|
|
|
|
raw(overrides_with = r#""no-ignore-global""#),
|
|
|
|
raw(hidden = "true")
|
|
|
|
)]
|
|
|
|
ignore_global: bool,
|
2019-07-12 23:39:38 -04:00
|
|
|
|
|
|
|
#[structopt(long, raw(overrides_with = r#""ignore-parent""#))]
|
|
|
|
/// Don't respect ignore files in parent directories.
|
|
|
|
no_ignore_parent: bool,
|
|
|
|
#[structopt(
|
|
|
|
long,
|
|
|
|
raw(overrides_with = r#""no-ignore-parent""#),
|
|
|
|
raw(hidden = "true")
|
|
|
|
)]
|
|
|
|
ignore_parent: bool,
|
2019-07-12 23:43:18 -04:00
|
|
|
|
|
|
|
#[structopt(long, raw(overrides_with = r#""ignore-vcs""#))]
|
|
|
|
/// Don't respect ignore files in vcs directories.
|
|
|
|
no_ignore_vcs: bool,
|
|
|
|
#[structopt(long, raw(overrides_with = r#""no-ignore-vcs""#), raw(hidden = "true"))]
|
|
|
|
ignore_vcs: bool,
|
2019-07-19 23:45:41 -04:00
|
|
|
|
|
|
|
#[structopt(flatten)]
|
|
|
|
verbose: clap_verbosity_flag::Verbosity,
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Options {
|
|
|
|
pub fn infer(mut self) -> Self {
|
2019-06-14 08:51:22 -04:00
|
|
|
if self.path.len() == 1 && self.path[0].is_file() {
|
|
|
|
self.threads = 1;
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
2019-07-10 09:21:02 -04:00
|
|
|
|
2019-07-19 09:24:15 -04:00
|
|
|
pub fn check_files(&self) -> Option<bool> {
|
|
|
|
match (self.check_files, self.no_check_files) {
|
|
|
|
(true, false) => Some(true),
|
|
|
|
(false, true) => Some(false),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-18 22:20:45 -04:00
|
|
|
pub fn check_filenames(&self) -> Option<bool> {
|
|
|
|
match (self.check_filenames, self.no_check_filenames) {
|
|
|
|
(true, false) => Some(true),
|
|
|
|
(false, true) => Some(false),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-13 21:24:27 -04:00
|
|
|
pub fn ignore_hex(&self) -> Option<bool> {
|
|
|
|
match (self.no_hex, self.hex) {
|
|
|
|
(true, false) => Some(false),
|
|
|
|
(false, true) => Some(true),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-13 22:14:06 -04:00
|
|
|
pub fn binary(&self) -> Option<bool> {
|
|
|
|
match (self.binary, self.no_binary) {
|
|
|
|
(true, false) => Some(true),
|
|
|
|
(false, true) => Some(false),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-10 09:21:02 -04:00
|
|
|
pub fn ignore_hidden(&self) -> Option<bool> {
|
|
|
|
match (self.hidden, self.no_hidden) {
|
|
|
|
(true, false) => Some(false),
|
|
|
|
(false, true) => Some(true),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
}
|
2019-07-10 22:12:14 -04:00
|
|
|
|
|
|
|
pub fn ignore_dot(&self) -> Option<bool> {
|
|
|
|
match (self.no_ignore_dot, self.ignore_dot) {
|
|
|
|
(true, false) => Some(false),
|
|
|
|
(false, true) => Some(true),
|
2019-07-11 23:56:27 -04:00
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
.or_else(|| self.ignore_files())
|
|
|
|
}
|
|
|
|
|
2019-07-12 23:36:32 -04:00
|
|
|
pub fn ignore_global(&self) -> Option<bool> {
|
|
|
|
match (self.no_ignore_global, self.ignore_global) {
|
|
|
|
(true, false) => Some(false),
|
|
|
|
(false, true) => Some(true),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
2019-07-12 23:43:18 -04:00
|
|
|
.or_else(|| self.ignore_vcs())
|
2019-07-12 23:36:32 -04:00
|
|
|
.or_else(|| self.ignore_files())
|
|
|
|
}
|
|
|
|
|
2019-07-12 23:39:38 -04:00
|
|
|
pub fn ignore_parent(&self) -> Option<bool> {
|
|
|
|
match (self.no_ignore_parent, self.ignore_parent) {
|
|
|
|
(true, false) => Some(false),
|
|
|
|
(false, true) => Some(true),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
.or_else(|| self.ignore_files())
|
|
|
|
}
|
|
|
|
|
2019-07-12 23:43:18 -04:00
|
|
|
pub fn ignore_vcs(&self) -> Option<bool> {
|
|
|
|
match (self.no_ignore_vcs, self.ignore_vcs) {
|
|
|
|
(true, false) => Some(false),
|
|
|
|
(false, true) => Some(true),
|
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
.or_else(|| self.ignore_files())
|
|
|
|
}
|
|
|
|
|
2019-07-11 23:56:27 -04:00
|
|
|
fn ignore_files(&self) -> Option<bool> {
|
|
|
|
match (self.no_ignore, self.ignore) {
|
|
|
|
(true, false) => Some(false),
|
|
|
|
(false, true) => Some(true),
|
2019-07-10 22:12:14 -04:00
|
|
|
(false, false) => None,
|
|
|
|
(_, _) => unreachable!("StructOpt should make this impossible"),
|
|
|
|
}
|
|
|
|
}
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|
|
|
|
|
2019-07-19 23:45:41 -04:00
|
|
|
pub fn get_logging(level: log::Level) -> env_logger::Builder {
|
|
|
|
let mut builder = env_logger::Builder::new();
|
|
|
|
|
|
|
|
builder.filter(None, level.to_level_filter());
|
|
|
|
|
|
|
|
if level == log::LevelFilter::Trace {
|
|
|
|
builder.default_format_timestamp(false);
|
|
|
|
} else {
|
|
|
|
builder.format(|f, record| {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"[{}] {}",
|
|
|
|
record.level().to_string().to_lowercase(),
|
|
|
|
record.args()
|
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
builder
|
|
|
|
}
|
|
|
|
|
2019-01-22 17:01:33 -05:00
|
|
|
fn run() -> Result<(), failure::Error> {
|
|
|
|
let options = Options::from_args().infer();
|
|
|
|
|
2019-07-19 23:45:41 -04:00
|
|
|
let mut builder = get_logging(options.verbose.log_level());
|
|
|
|
builder.init();
|
|
|
|
|
2019-07-03 21:22:36 -04:00
|
|
|
let dictionary = typos::Dictionary::new();
|
2019-07-18 22:20:45 -04:00
|
|
|
let check_filenames = options.check_filenames().unwrap_or(true);
|
2019-07-19 09:24:15 -04:00
|
|
|
let check_files = options.check_files().unwrap_or(true);
|
2019-07-13 21:24:27 -04:00
|
|
|
let ignore_hex = options.ignore_hex().unwrap_or(true);
|
2019-07-13 22:14:06 -04:00
|
|
|
let binary = options.binary().unwrap_or(false);
|
2019-01-22 17:01:33 -05:00
|
|
|
|
2019-06-14 08:43:21 -04:00
|
|
|
let first_path = &options
|
|
|
|
.path
|
|
|
|
.get(0)
|
|
|
|
.expect("arg parsing enforces at least one");
|
2019-01-22 17:01:33 -05:00
|
|
|
let mut walk = ignore::WalkBuilder::new(first_path);
|
|
|
|
for path in &options.path[1..] {
|
|
|
|
walk.add(path);
|
|
|
|
}
|
2019-07-10 09:21:02 -04:00
|
|
|
walk.threads(options.threads)
|
2019-07-10 22:12:14 -04:00
|
|
|
.hidden(options.ignore_hidden().unwrap_or(true))
|
2019-07-12 23:36:32 -04:00
|
|
|
.ignore(options.ignore_dot().unwrap_or(true))
|
2019-07-12 23:39:38 -04:00
|
|
|
.git_global(options.ignore_global().unwrap_or(true))
|
2019-07-12 23:43:18 -04:00
|
|
|
.git_ignore(options.ignore_vcs().unwrap_or(true))
|
|
|
|
.git_exclude(options.ignore_vcs().unwrap_or(true))
|
2019-07-12 23:39:38 -04:00
|
|
|
.parents(options.ignore_parent().unwrap_or(true));
|
2019-01-22 17:01:33 -05:00
|
|
|
// TODO Add build_parallel for options.threads != 1
|
|
|
|
for entry in walk.build() {
|
|
|
|
let entry = entry?;
|
|
|
|
if entry.file_type().map(|t| t.is_file()).unwrap_or(true) {
|
2019-07-13 21:24:27 -04:00
|
|
|
typos::process_file(
|
|
|
|
entry.path(),
|
|
|
|
&dictionary,
|
2019-07-18 22:20:45 -04:00
|
|
|
check_filenames,
|
2019-07-19 09:24:15 -04:00
|
|
|
check_files,
|
2019-07-13 21:24:27 -04:00
|
|
|
ignore_hex,
|
2019-07-13 22:14:06 -04:00
|
|
|
binary,
|
2019-07-13 21:24:27 -04:00
|
|
|
options.format.report(),
|
|
|
|
)?;
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
run().unwrap();
|
|
|
|
}
|