typos/src/main.rs

203 lines
6.3 KiB
Rust
Raw Normal View History

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;
use std::sync::atomic;
2019-07-19 23:45:41 -04:00
2019-01-22 17:01:33 -05:00
use structopt::StructOpt;
2020-03-23 21:33:59 -04:00
mod args;
2020-03-23 21:37:06 -04:00
mod checks;
mod config;
mod dict;
mod replace;
2020-03-23 21:38:11 -04:00
fn main() {
let code = match run() {
Ok(code) => code,
Err(err) => {
eprintln!("{}", err);
1
}
};
2020-03-23 21:38:11 -04:00
std::process::exit(code);
2019-07-19 23:45:41 -04:00
}
2019-10-29 13:36:50 -04:00
fn run() -> Result<i32, anyhow::Error> {
2020-03-23 21:33:59 -04:00
let args = args::Args::from_args();
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() {
config::Config::from_file(path)?
} else {
config::Config::default()
};
let mut typos_found = false;
let mut errors_found = false;
2019-08-07 12:09:01 -04:00
for path in args.path.iter() {
let path = path.canonicalize()?;
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 {
let derived = config::Config::derive(cwd)?;
config.update(&derived);
}
2019-08-07 12:09:01 -04:00
config.update(&args.config);
config.default.update(&args.overrides);
let config = config;
let parser = typos::tokens::ParserBuilder::new()
.ignore_hex(config.default.ignore_hex())
.leading_digits(config.default.identifier_leading_digits())
.leading_chars(config.default.identifier_leading_chars().to_owned())
.include_digits(config.default.identifier_include_digits())
.include_chars(config.default.identifier_include_chars().to_owned())
.build();
let dictionary = crate::dict::BuiltIn::new(config.default.locale());
let mut dictionary = crate::dict::Override::new(dictionary);
dictionary.identifiers(config.default.extend_identifiers());
dictionary.words(config.default.extend_words());
2019-11-14 22:09:56 -05:00
let mut settings = typos::checks::TyposSettings::new();
settings
.check_filenames(config.default.check_filename())
.check_files(config.default.check_file())
.binary(config.files.binary());
let threads = if path.is_file() { 1 } else { args.threads };
let single_threaded = threads == 1;
let mut walk = ignore::WalkBuilder::new(path);
2019-10-25 18:34:21 -04:00
walk.threads(args.threads)
.hidden(config.files.ignore_hidden())
.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());
let mut reporter = args.format.reporter();
let replace_reporter = replace::Replace::new(reporter);
if args.write_changes {
reporter = &replace_reporter;
}
if args.files {
if single_threaded {
for entry in walk.build() {
match entry {
Ok(entry) => {
let msg = typos::report::File::new(entry.path());
reporter.report(msg.into());
}
Err(err) => {
let msg = typos::report::Error::new(err.to_string());
reporter.report(msg.into());
errors_found = true
}
}
}
} else {
let atomic_errors = atomic::AtomicBool::new(errors_found);
walk.build_parallel().run(|| {
Box::new(|entry: Result<ignore::DirEntry, ignore::Error>| {
match entry {
Ok(entry) => {
let msg = typos::report::File::new(entry.path());
reporter.report(msg.into());
}
Err(err) => {
let msg = typos::report::Error::new(err.to_string());
reporter.report(msg.into());
atomic_errors.store(true, atomic::Ordering::Relaxed);
}
}
ignore::WalkState::Continue
})
});
errors_found = atomic_errors.into_inner();
}
2020-03-23 19:39:45 -04:00
} else {
let (identifier_parser, word_parser, checks);
2020-03-23 21:37:06 -04:00
let selected_checks: &dyn checks::Checks = if args.identifiers {
2020-03-23 19:39:45 -04:00
identifier_parser = settings.build_identifier_parser();
&identifier_parser
} else if args.words {
word_parser = settings.build_word_parser();
&word_parser
} else {
2020-03-23 19:39:45 -04:00
checks = settings.build_checks();
&checks
};
2020-03-23 19:39:45 -04:00
let (cur_typos, cur_errors) = if single_threaded {
2020-03-23 21:37:06 -04:00
checks::check_path(
2020-03-23 19:39:45 -04:00
walk.build(),
selected_checks,
&parser,
&dictionary,
reporter,
)
} else {
2020-03-23 21:37:06 -04:00
checks::check_path_parallel(
walk.build_parallel(),
2020-03-23 19:39:45 -04:00
selected_checks,
&parser,
&dictionary,
reporter,
)
};
2019-11-15 09:48:07 -05:00
if cur_typos {
typos_found = true;
}
if cur_errors {
errors_found = true;
}
2019-01-22 17:01:33 -05:00
}
if args.write_changes {
replace_reporter.write()?;
}
2019-01-22 17:01:33 -05:00
}
if errors_found {
Ok(2)
} else if typos_found {
Ok(1)
} else {
Ok(0)
}
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
}