mirror of
https://github.com/crate-ci/typos.git
synced 2024-11-29 04:21:06 -05:00
refactor(cli): Pull out policy creation
This commit is contained in:
parent
f402d3ee77
commit
a76ddd42ce
5 changed files with 217 additions and 69 deletions
|
@ -101,7 +101,7 @@ pub(crate) struct Args {
|
|||
pub(crate) verbose: clap_verbosity_flag::Verbosity,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[derive(Debug, Clone, StructOpt)]
|
||||
#[structopt(rename_all = "kebab-case")]
|
||||
pub(crate) struct FileArgs {
|
||||
#[structopt(long, overrides_with("no-binary"))]
|
||||
|
|
|
@ -117,6 +117,17 @@ pub struct Config {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_dir(cwd: &std::path::Path) -> Result<Option<Self>, anyhow::Error> {
|
||||
let config = if let Some(path) =
|
||||
find_project_file(cwd, &["typos.toml", "_typos.toml", ".typos.toml"])
|
||||
{
|
||||
Some(Self::from_file(&path)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn from_file(path: &std::path::Path) -> Result<Self, anyhow::Error> {
|
||||
let s = std::fs::read_to_string(path)?;
|
||||
Self::from_toml(&s)
|
||||
|
@ -134,14 +145,6 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn derive(cwd: &std::path::Path) -> Result<Self, anyhow::Error> {
|
||||
if let Some(path) = find_project_file(cwd, &["typos.toml", "_typos.toml", ".typos.toml"]) {
|
||||
Self::from_file(&path)
|
||||
} else {
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, source: &dyn ConfigSource) {
|
||||
if let Some(walk) = source.walk() {
|
||||
self.files.update(walk);
|
||||
|
@ -522,15 +525,13 @@ impl DictSource for DictConfig {
|
|||
}
|
||||
|
||||
fn find_project_file(dir: &std::path::Path, names: &[&str]) -> Option<std::path::PathBuf> {
|
||||
for ancestor in dir.ancestors() {
|
||||
let mut file_path = ancestor.join("placeholder");
|
||||
let mut file_path = dir.join("placeholder");
|
||||
for name in names {
|
||||
file_path.set_file_name(name);
|
||||
if file_path.exists() {
|
||||
return Some(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
13
src/file.rs
13
src/file.rs
|
@ -552,11 +552,11 @@ fn fix_buffer(mut buffer: Vec<u8>, typos: impl Iterator<Item = typos::Typo<'stat
|
|||
pub fn walk_path(
|
||||
walk: ignore::Walk,
|
||||
checks: &dyn FileChecker,
|
||||
policy: &crate::policy::Policy,
|
||||
engine: &crate::policy::ConfigEngine,
|
||||
reporter: &dyn report::Report,
|
||||
) -> Result<(), ignore::Error> {
|
||||
for entry in walk {
|
||||
walk_entry(entry, checks, policy, reporter)?;
|
||||
walk_entry(entry, checks, engine, reporter)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -564,13 +564,13 @@ pub fn walk_path(
|
|||
pub fn walk_path_parallel(
|
||||
walk: ignore::WalkParallel,
|
||||
checks: &dyn FileChecker,
|
||||
policy: &crate::policy::Policy,
|
||||
engine: &crate::policy::ConfigEngine,
|
||||
reporter: &dyn report::Report,
|
||||
) -> Result<(), ignore::Error> {
|
||||
let error: std::sync::Mutex<Result<(), ignore::Error>> = std::sync::Mutex::new(Ok(()));
|
||||
walk.run(|| {
|
||||
Box::new(|entry: Result<ignore::DirEntry, ignore::Error>| {
|
||||
match walk_entry(entry, checks, policy, reporter) {
|
||||
match walk_entry(entry, checks, engine, reporter) {
|
||||
Ok(()) => ignore::WalkState::Continue,
|
||||
Err(err) => {
|
||||
*error.lock().unwrap() = Err(err);
|
||||
|
@ -586,7 +586,7 @@ pub fn walk_path_parallel(
|
|||
fn walk_entry(
|
||||
entry: Result<ignore::DirEntry, ignore::Error>,
|
||||
checks: &dyn FileChecker,
|
||||
policy: &crate::policy::Policy,
|
||||
engine: &crate::policy::ConfigEngine,
|
||||
reporter: &dyn report::Report,
|
||||
) -> Result<(), ignore::Error> {
|
||||
let entry = match entry {
|
||||
|
@ -603,7 +603,8 @@ fn walk_entry(
|
|||
} else {
|
||||
entry.path()
|
||||
};
|
||||
checks.check_file(path, explicit, policy, reporter)?;
|
||||
let policy = engine.policy(path);
|
||||
checks.check_file(path, explicit, &policy, reporter)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
52
src/main.rs
52
src/main.rs
|
@ -57,7 +57,19 @@ fn run_dump_config(args: &args::Args, output_path: &std::path::Path) -> proc_exi
|
|||
path.as_path()
|
||||
};
|
||||
|
||||
let config = load_config(cwd, &args).with_code(proc_exit::Code::CONFIG_ERR)?;
|
||||
let storage = typos_cli::policy::ConfigStorage::new();
|
||||
let mut overrides = config::EngineConfig::default();
|
||||
overrides.update(&args.overrides);
|
||||
let mut engine = typos_cli::policy::ConfigEngine::new(&storage);
|
||||
engine.set_isolated(args.isolated).set_overrides(overrides);
|
||||
if let Some(path) = args.custom_config.as_ref() {
|
||||
let custom = config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)?;
|
||||
engine.set_custom_config(custom);
|
||||
}
|
||||
let config = engine
|
||||
.load_config(cwd)
|
||||
.with_code(proc_exit::Code::CONFIG_ERR)?;
|
||||
|
||||
let mut defaulted_config = config::Config::from_defaults();
|
||||
defaulted_config.update(&config);
|
||||
let output = toml::to_string_pretty(&defaulted_config).with_code(proc_exit::Code::FAILURE)?;
|
||||
|
@ -88,12 +100,21 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult {
|
|||
} else {
|
||||
path.as_path()
|
||||
};
|
||||
let config = load_config(cwd, &args).with_code(proc_exit::Code::CONFIG_ERR)?;
|
||||
|
||||
let storage = typos_cli::policy::ConfigStorage::new();
|
||||
let engine = typos_cli::policy::ConfigEngine::new(config, &storage);
|
||||
let files = engine.files();
|
||||
let policy = engine.policy();
|
||||
let mut overrides = config::EngineConfig::default();
|
||||
overrides.update(&args.overrides);
|
||||
let mut engine = typos_cli::policy::ConfigEngine::new(&storage);
|
||||
engine.set_isolated(args.isolated).set_overrides(overrides);
|
||||
if let Some(path) = args.custom_config.as_ref() {
|
||||
let custom = config::Config::from_file(path).with_code(proc_exit::Code::CONFIG_ERR)?;
|
||||
engine.set_custom_config(custom);
|
||||
}
|
||||
|
||||
engine
|
||||
.init_dir(cwd)
|
||||
.with_code(proc_exit::Code::CONFIG_ERR)?;
|
||||
let files = engine.files(cwd);
|
||||
|
||||
let threads = if path.is_file() { 1 } else { args.threads };
|
||||
let single_threaded = threads == 1;
|
||||
|
@ -131,12 +152,12 @@ fn run_checks(args: &args::Args) -> proc_exit::ExitResult {
|
|||
};
|
||||
|
||||
if single_threaded {
|
||||
typos_cli::file::walk_path(walk.build(), selected_checks, &policy, reporter)
|
||||
typos_cli::file::walk_path(walk.build(), selected_checks, &engine, reporter)
|
||||
} else {
|
||||
typos_cli::file::walk_path_parallel(
|
||||
walk.build_parallel(),
|
||||
selected_checks,
|
||||
&policy,
|
||||
&engine,
|
||||
reporter,
|
||||
)
|
||||
}
|
||||
|
@ -189,20 +210,3 @@ fn init_logging(level: Option<log::Level>) {
|
|||
builder.init();
|
||||
}
|
||||
}
|
||||
|
||||
fn load_config(cwd: &std::path::Path, args: &args::Args) -> Result<config::Config, anyhow::Error> {
|
||||
let mut config = config::Config::default();
|
||||
|
||||
if !args.isolated {
|
||||
let derived = config::Config::derive(cwd)?;
|
||||
config.update(&derived);
|
||||
}
|
||||
if let Some(path) = args.custom_config.as_ref() {
|
||||
config.update(&config::Config::from_file(path)?);
|
||||
}
|
||||
|
||||
config.update(&args.config);
|
||||
config.default.update(&args.overrides);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
|
186
src/policy.rs
186
src/policy.rs
|
@ -1,30 +1,142 @@
|
|||
pub struct ConfigStorage {
|
||||
arena: typed_arena::Arena<kstring::KString>,
|
||||
arena: std::sync::Mutex<typed_arena::Arena<kstring::KString>>,
|
||||
}
|
||||
|
||||
impl ConfigStorage {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
arena: typed_arena::Arena::new(),
|
||||
arena: std::sync::Mutex::new(typed_arena::Arena::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get<'s>(&'s self, other: &str) -> &'s str {
|
||||
self.arena.alloc(kstring::KString::from_ref(other))
|
||||
// Safe because we the references are stable once created.
|
||||
//
|
||||
// Trying to get this handled inside of `typed_arena` directly, see
|
||||
// https://github.com/SimonSapin/rust-typed-arena/issues/49#issuecomment-809517312
|
||||
unsafe {
|
||||
std::mem::transmute::<&str, &str>(
|
||||
self.arena
|
||||
.lock()
|
||||
.unwrap()
|
||||
.alloc(kstring::KString::from_ref(other)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfigEngine<'s> {
|
||||
files: crate::config::Walk,
|
||||
check_filenames: bool,
|
||||
check_files: bool,
|
||||
binary: bool,
|
||||
tokenizer: typos::tokens::Tokenizer,
|
||||
dict: crate::dict::Override<'s, 's, crate::dict::BuiltIn>,
|
||||
storage: &'s ConfigStorage,
|
||||
|
||||
overrides: Option<crate::config::EngineConfig>,
|
||||
custom: Option<crate::config::Config>,
|
||||
isolated: bool,
|
||||
|
||||
configs: std::collections::HashMap<std::path::PathBuf, DirConfig>,
|
||||
files: Intern<crate::config::Walk>,
|
||||
tokenizer: Intern<typos::tokens::Tokenizer>,
|
||||
dict: Intern<crate::dict::Override<'s, 's, crate::dict::BuiltIn>>,
|
||||
}
|
||||
|
||||
impl<'s> ConfigEngine<'s> {
|
||||
pub fn new(config: crate::config::Config, storage: &'s ConfigStorage) -> Self {
|
||||
pub fn new(storage: &'s ConfigStorage) -> Self {
|
||||
Self {
|
||||
storage,
|
||||
overrides: Default::default(),
|
||||
custom: Default::default(),
|
||||
configs: Default::default(),
|
||||
isolated: false,
|
||||
files: Default::default(),
|
||||
tokenizer: Default::default(),
|
||||
dict: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_overrides(&mut self, overrides: crate::config::EngineConfig) -> &mut Self {
|
||||
self.overrides = Some(overrides);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_custom_config(&mut self, custom: crate::config::Config) -> &mut Self {
|
||||
self.custom = Some(custom);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_isolated(&mut self, isolated: bool) -> &mut Self {
|
||||
self.isolated = isolated;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn files(&mut self, cwd: &std::path::Path) -> &crate::config::Walk {
|
||||
let dir = self
|
||||
.configs
|
||||
.get(cwd)
|
||||
.expect("`init_dir` must be called first");
|
||||
self.get_files(dir)
|
||||
}
|
||||
|
||||
pub fn policy(&self, path: &std::path::Path) -> Policy<'_, '_> {
|
||||
let dir = self
|
||||
.get_dir(path)
|
||||
.expect("`files()` should be called first");
|
||||
Policy {
|
||||
check_filenames: dir.check_filenames,
|
||||
check_files: dir.check_files,
|
||||
binary: dir.binary,
|
||||
tokenizer: self.get_tokenizer(dir),
|
||||
dict: self.get_dict(dir),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_files(&self, dir: &DirConfig) -> &crate::config::Walk {
|
||||
self.files.get(dir.files)
|
||||
}
|
||||
|
||||
fn get_tokenizer(&self, dir: &DirConfig) -> &typos::tokens::Tokenizer {
|
||||
self.tokenizer.get(dir.tokenizer)
|
||||
}
|
||||
|
||||
fn get_dict(&self, dir: &DirConfig) -> &dyn typos::Dictionary {
|
||||
self.dict.get(dir.dict)
|
||||
}
|
||||
|
||||
fn get_dir(&self, path: &std::path::Path) -> Option<&DirConfig> {
|
||||
for path in path.ancestors() {
|
||||
if let Some(dir) = self.configs.get(path) {
|
||||
return Some(dir);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn load_config(
|
||||
&self,
|
||||
cwd: &std::path::Path,
|
||||
) -> Result<crate::config::Config, anyhow::Error> {
|
||||
let mut config = crate::config::Config::default();
|
||||
|
||||
if !self.isolated {
|
||||
if let Some(derived) = crate::config::Config::from_dir(cwd)? {
|
||||
config.update(&derived);
|
||||
}
|
||||
}
|
||||
if let Some(custom) = self.custom.as_ref() {
|
||||
config.update(custom);
|
||||
}
|
||||
if let Some(overrides) = self.overrides.as_ref() {
|
||||
config.default.update(overrides);
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn init_dir(&mut self, cwd: &std::path::Path) -> Result<(), anyhow::Error> {
|
||||
if self.configs.contains_key(cwd) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let config = self.load_config(cwd)?;
|
||||
|
||||
let crate::config::Config { files, default } = config;
|
||||
let binary = default.binary();
|
||||
let check_filename = default.check_filename();
|
||||
|
@ -49,37 +161,67 @@ impl<'s> ConfigEngine<'s> {
|
|||
dict.identifiers(
|
||||
dict_config
|
||||
.extend_identifiers()
|
||||
.map(|(k, v)| (storage.get(k), storage.get(v))),
|
||||
.map(|(k, v)| (self.storage.get(k), self.storage.get(v))),
|
||||
);
|
||||
dict.words(
|
||||
dict_config
|
||||
.extend_words()
|
||||
.map(|(k, v)| (storage.get(k), storage.get(v))),
|
||||
.map(|(k, v)| (self.storage.get(k), self.storage.get(v))),
|
||||
);
|
||||
|
||||
Self {
|
||||
let dict = self.dict.intern(dict);
|
||||
let files = self.files.intern(files);
|
||||
let tokenizer = self.tokenizer.intern(tokenizer);
|
||||
|
||||
let dir = DirConfig {
|
||||
files,
|
||||
check_filenames: check_filename,
|
||||
check_files: check_file,
|
||||
binary: binary,
|
||||
tokenizer,
|
||||
dict,
|
||||
};
|
||||
|
||||
self.configs.insert(cwd.to_owned(), dir);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn files(&self) -> &crate::config::Walk {
|
||||
&self.files
|
||||
struct Intern<T> {
|
||||
data: Vec<T>,
|
||||
}
|
||||
|
||||
pub fn policy(&self) -> Policy<'_, '_> {
|
||||
Policy {
|
||||
check_filenames: self.check_filenames,
|
||||
check_files: self.check_files,
|
||||
binary: self.binary,
|
||||
tokenizer: &self.tokenizer,
|
||||
dict: &self.dict,
|
||||
impl<T> Intern<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intern(&mut self, value: T) -> usize {
|
||||
let symbol = self.data.len();
|
||||
self.data.push(value);
|
||||
symbol
|
||||
}
|
||||
|
||||
pub fn get(&self, symbol: usize) -> &T {
|
||||
&self.data[symbol]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Intern<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
struct DirConfig {
|
||||
files: usize,
|
||||
tokenizer: usize,
|
||||
dict: usize,
|
||||
check_filenames: bool,
|
||||
check_files: bool,
|
||||
binary: bool,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
|
|
Loading…
Reference in a new issue