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;
|
|
|
|
|
2020-03-23 21:33:59 -04:00
|
|
|
mod args;
|
2020-12-30 20:41:08 -05:00
|
|
|
use typos_cli::checks;
|
|
|
|
use typos_cli::config;
|
|
|
|
use typos_cli::dict;
|
|
|
|
use typos_cli::diff;
|
|
|
|
use typos_cli::replace;
|
|
|
|
use typos_cli::report;
|
2019-08-07 10:40:06 -04:00
|
|
|
|
2020-11-16 21:02:10 -05:00
|
|
|
use proc_exit::WithCodeResultExt;
|
2020-11-14 20:53:51 -05:00
|
|
|
|
2020-03-23 21:38:11 -04:00
|
|
|
fn main() {
|
2020-11-23 13:40:55 -05:00
|
|
|
human_panic::setup_panic!();
|
2020-11-23 11:08:38 -05:00
|
|
|
let result = run();
|
|
|
|
proc_exit::exit(result);
|
2019-07-19 23:45:41 -04:00
|
|
|
}
|
|
|
|
|
2020-11-23 11:08:38 -05:00
|
|
|
fn run() -> proc_exit::ExitResult {
|
2020-11-14 20:53:51 -05:00
|
|
|
// clap's `get_matches` uses Failure rather than Usage, so bypass it for `get_matches_safe`.
|
|
|
|
let args = match args::Args::from_args_safe() {
|
|
|
|
Ok(args) => args,
|
|
|
|
Err(e) if e.use_stderr() => {
|
2020-11-16 21:02:10 -05:00
|
|
|
return Err(proc_exit::Code::USAGE_ERR.with_message(e));
|
2020-11-14 20:53:51 -05:00
|
|
|
}
|
|
|
|
Err(e) => {
|
2020-11-16 21:02:10 -05:00
|
|
|
writeln!(std::io::stdout(), "{}", e)?;
|
2020-11-23 11:08:38 -05:00
|
|
|
return proc_exit::Code::SUCCESS.ok();
|
2020-11-14 20:53:51 -05:00
|
|
|
}
|
|
|
|
};
|
2019-01-22 17:01:33 -05:00
|
|
|
|
2019-10-17 22:49:26 -04:00
|
|
|
init_logging(args.verbose.log_level());
|
2019-07-19 23:45:41 -04:00
|
|
|
|
2020-03-21 15:33:51 -04:00
|
|
|
let config = if let Some(path) = args.custom_config.as_ref() {
|
2020-11-16 21:02:10 -05:00
|
|
|
config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)?
|
2020-03-21 15:33:51 -04:00
|
|
|
} else {
|
|
|
|
config::Config::default()
|
|
|
|
};
|
2019-08-07 11:16:57 -04:00
|
|
|
|
2019-07-22 16:33:39 -04:00
|
|
|
let mut typos_found = false;
|
2019-10-26 22:31:10 -04:00
|
|
|
let mut errors_found = false;
|
2019-08-07 12:09:01 -04:00
|
|
|
for path in args.path.iter() {
|
2020-11-16 21:02:10 -05:00
|
|
|
let path = path.canonicalize().with_code(proc_exit::Code::USAGE_ERR)?;
|
2019-08-07 11:16:57 -04:00
|
|
|
let cwd = if path.is_file() {
|
|
|
|
path.parent().unwrap()
|
|
|
|
} else {
|
|
|
|
path.as_path()
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut config = config.clone();
|
2019-08-07 12:09:01 -04:00
|
|
|
if !args.isolated {
|
2020-11-16 21:02:10 -05:00
|
|
|
let derived = config::Config::derive(cwd).with_code(proc_exit::Code::CONFIG_ERR)?;
|
2019-08-07 11:16:57 -04:00
|
|
|
config.update(&derived);
|
|
|
|
}
|
2019-08-07 12:09:01 -04:00
|
|
|
config.update(&args.config);
|
2019-08-08 09:22:46 -04:00
|
|
|
config.default.update(&args.overrides);
|
2019-08-07 11:16:57 -04:00
|
|
|
let config = config;
|
|
|
|
|
2020-12-28 22:51:44 -05:00
|
|
|
let parser = typos::tokens::TokenizerBuilder::new()
|
2019-08-08 09:22:46 -04:00
|
|
|
.ignore_hex(config.default.ignore_hex())
|
2019-11-02 11:15:51 -04:00
|
|
|
.leading_digits(config.default.identifier_leading_digits())
|
|
|
|
.leading_chars(config.default.identifier_leading_chars().to_owned())
|
2019-08-08 09:37:06 -04:00
|
|
|
.include_digits(config.default.identifier_include_digits())
|
|
|
|
.include_chars(config.default.identifier_include_chars().to_owned())
|
2019-08-07 18:14:29 -04:00
|
|
|
.build();
|
|
|
|
|
2020-05-27 21:46:41 -04:00
|
|
|
let dictionary = crate::dict::BuiltIn::new(config.default.locale());
|
2020-09-02 21:12:49 -04:00
|
|
|
let mut dictionary = crate::dict::Override::new(dictionary);
|
2020-10-24 22:17:16 -04:00
|
|
|
dictionary.identifiers(config.default.extend_identifiers());
|
|
|
|
dictionary.words(config.default.extend_words());
|
2019-11-14 22:09:56 -05:00
|
|
|
|
2020-12-30 20:41:08 -05:00
|
|
|
let mut settings = checks::TyposSettings::new();
|
2019-10-30 09:26:59 -04:00
|
|
|
settings
|
2019-08-08 09:22:46 -04:00
|
|
|
.check_filenames(config.default.check_filename())
|
|
|
|
.check_files(config.default.check_file())
|
2019-10-30 09:26:59 -04:00
|
|
|
.binary(config.files.binary());
|
2019-08-07 18:14:29 -04:00
|
|
|
|
2020-03-23 20:03:15 -04:00
|
|
|
let threads = if path.is_file() { 1 } else { args.threads };
|
|
|
|
let single_threaded = threads == 1;
|
|
|
|
|
2019-08-07 11:16:57 -04:00
|
|
|
let mut walk = ignore::WalkBuilder::new(path);
|
2019-10-25 18:34:21 -04:00
|
|
|
walk.threads(args.threads)
|
|
|
|
.hidden(config.files.ignore_hidden())
|
2019-08-07 12:05:19 -04:00
|
|
|
.ignore(config.files.ignore_dot())
|
|
|
|
.git_global(config.files.ignore_global())
|
|
|
|
.git_ignore(config.files.ignore_vcs())
|
|
|
|
.git_exclude(config.files.ignore_vcs())
|
|
|
|
.parents(config.files.ignore_parent());
|
2020-03-23 19:31:15 -04:00
|
|
|
|
2020-11-16 21:02:10 -05:00
|
|
|
// HACK: Diff doesn't handle mixing content
|
|
|
|
let output_reporter = if args.diff {
|
|
|
|
&args::PRINT_SILENT
|
|
|
|
} else {
|
|
|
|
args.format.reporter()
|
|
|
|
};
|
2020-12-30 20:41:08 -05:00
|
|
|
let status_reporter = report::MessageStatus::new(output_reporter);
|
|
|
|
let mut reporter: &dyn report::Report = &status_reporter;
|
2020-03-24 07:11:31 -04:00
|
|
|
let replace_reporter = replace::Replace::new(reporter);
|
2020-11-11 19:19:26 -05:00
|
|
|
let diff_reporter = diff::Diff::new(reporter);
|
|
|
|
if args.diff {
|
|
|
|
reporter = &diff_reporter;
|
|
|
|
} else if args.write_changes {
|
2020-03-24 07:11:31 -04:00
|
|
|
reporter = &replace_reporter;
|
|
|
|
}
|
2020-03-23 19:31:15 -04:00
|
|
|
|
2020-11-10 07:26:42 -05:00
|
|
|
let (files, identifier_parser, word_parser, checks);
|
2020-12-30 20:41:08 -05:00
|
|
|
let selected_checks: &dyn checks::Check = if args.files {
|
2020-11-10 07:26:42 -05:00
|
|
|
files = settings.build_files();
|
|
|
|
&files
|
|
|
|
} else if args.identifiers {
|
|
|
|
identifier_parser = settings.build_identifier_parser();
|
|
|
|
&identifier_parser
|
|
|
|
} else if args.words {
|
|
|
|
word_parser = settings.build_word_parser();
|
|
|
|
&word_parser
|
2020-03-23 19:39:45 -04:00
|
|
|
} else {
|
2020-11-10 07:26:42 -05:00
|
|
|
checks = settings.build_typos();
|
|
|
|
&checks
|
|
|
|
};
|
|
|
|
|
2020-11-16 21:02:10 -05:00
|
|
|
if single_threaded {
|
2020-11-10 07:26:42 -05:00
|
|
|
checks::check_path(
|
|
|
|
walk.build(),
|
|
|
|
selected_checks,
|
|
|
|
&parser,
|
|
|
|
&dictionary,
|
|
|
|
reporter,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
checks::check_path_parallel(
|
|
|
|
walk.build_parallel(),
|
|
|
|
selected_checks,
|
|
|
|
&parser,
|
|
|
|
&dictionary,
|
|
|
|
reporter,
|
|
|
|
)
|
2020-11-16 21:02:10 -05:00
|
|
|
}
|
2020-11-23 11:08:38 -05:00
|
|
|
.map_err(|e| {
|
|
|
|
e.io_error()
|
|
|
|
.map(|i| proc_exit::Code::from(i.kind()))
|
|
|
|
.unwrap_or_default()
|
|
|
|
.with_message(e)
|
|
|
|
})?;
|
2020-11-16 21:02:10 -05:00
|
|
|
if status_reporter.typos_found() {
|
2020-11-10 07:26:42 -05:00
|
|
|
typos_found = true;
|
|
|
|
}
|
2020-11-16 21:02:10 -05:00
|
|
|
if status_reporter.errors_found() {
|
2020-11-10 07:26:42 -05:00
|
|
|
errors_found = true;
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|
2020-03-24 07:11:31 -04:00
|
|
|
|
2020-11-11 19:19:26 -05:00
|
|
|
if args.diff {
|
2020-11-16 21:02:10 -05:00
|
|
|
diff_reporter.show().with_code(proc_exit::Code::FAILURE)?;
|
2020-11-11 19:19:26 -05:00
|
|
|
} else if args.write_changes {
|
2020-11-16 21:02:10 -05:00
|
|
|
replace_reporter
|
|
|
|
.write()
|
|
|
|
.with_code(proc_exit::Code::FAILURE)?;
|
2020-03-24 07:11:31 -04:00
|
|
|
}
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|
|
|
|
|
2019-10-26 22:31:10 -04:00
|
|
|
if errors_found {
|
2020-11-16 21:02:10 -05:00
|
|
|
proc_exit::Code::FAILURE.ok()
|
2019-10-26 22:31:10 -04:00
|
|
|
} else if typos_found {
|
2020-11-16 21:02:10 -05:00
|
|
|
// Can;'t use `Failure` since its so prevalent, it could be easy to get a
|
|
|
|
// `Failure` from something else and get it mixed up with typos.
|
|
|
|
//
|
|
|
|
// Can't use DataErr or anything else an std::io::ErrorKind might map to.
|
|
|
|
proc_exit::Code::UNKNOWN.ok()
|
2019-07-22 16:33:39 -04:00
|
|
|
} else {
|
2020-11-16 21:02:10 -05:00
|
|
|
proc_exit::Code::SUCCESS.ok()
|
2019-07-22 16:33:39 -04:00
|
|
|
}
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|
|
|
|
|
2020-03-23 21:38:11 -04:00
|
|
|
fn init_logging(level: Option<log::Level>) {
|
|
|
|
if let Some(level) = level {
|
|
|
|
let mut builder = env_logger::Builder::new();
|
|
|
|
|
|
|
|
builder.filter(None, level.to_level_filter());
|
|
|
|
|
|
|
|
if level == log::LevelFilter::Trace {
|
|
|
|
builder.format_timestamp_secs();
|
|
|
|
} else {
|
|
|
|
builder.format(|f, record| {
|
|
|
|
writeln!(
|
|
|
|
f,
|
|
|
|
"[{}] {}",
|
|
|
|
record.level().to_string().to_lowercase(),
|
|
|
|
record.args()
|
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.init();
|
|
|
|
}
|
2019-01-22 17:01:33 -05:00
|
|
|
}
|