diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..572d4db --- /dev/null +++ b/src/config.rs @@ -0,0 +1,126 @@ +pub trait ConfigSource { + /// Skip hidden files and directories. + fn ignore_hidden(&self) -> Option { + None + } + + /// Respect ignore files. + fn ignore_files(&self) -> Option { + None + } + + /// Respect .ignore files. + fn ignore_dot(&self) -> Option { + None + } + + /// Respect ignore files in vcs directories. + fn ignore_vcs(&self) -> Option { + None + } + + /// Respect global ignore files. + fn ignore_global(&self) -> Option { + None + } + + /// Respect ignore files in parent directories. + fn ignore_parent(&self) -> Option { + None + } +} + +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields, default)] +#[serde(rename_all = "kebab-case")] +pub struct Config { + pub ignore_hidden: Option, + pub ignore_files: Option, + pub ignore_dot: Option, + pub ignore_vcs: Option, + pub ignore_global: Option, + pub ignore_parent: Option, +} + +impl Config { + pub fn update(&mut self, source: &dyn ConfigSource) { + if let Some(source) = source.ignore_hidden() { + self.ignore_hidden = Some(source); + } + if let Some(source) = source.ignore_files() { + self.ignore_files = Some(source); + self.ignore_dot = None; + self.ignore_vcs = None; + self.ignore_global = None; + self.ignore_parent = None; + } + if let Some(source) = source.ignore_dot() { + self.ignore_dot = Some(source); + } + if let Some(source) = source.ignore_vcs() { + self.ignore_vcs = Some(source); + self.ignore_global = None; + } + if let Some(source) = source.ignore_global() { + self.ignore_global = Some(source); + } + if let Some(source) = source.ignore_parent() { + self.ignore_parent = Some(source); + } + } + + pub fn ignore_hidden(&self) -> bool { + self.ignore_hidden.unwrap_or(true) + } + + pub fn ignore_dot(&self) -> bool { + self.ignore_dot + .or_else(|| self.ignore_files) + .unwrap_or(true) + } + + pub fn ignore_vcs(&self) -> bool { + self.ignore_vcs + .or_else(|| self.ignore_files) + .unwrap_or(true) + } + + pub fn ignore_global(&self) -> bool { + self.ignore_global + .or_else(|| self.ignore_vcs) + .or_else(|| self.ignore_files) + .unwrap_or(true) + } + + pub fn ignore_parent(&self) -> bool { + self.ignore_parent + .or_else(|| self.ignore_files) + .unwrap_or(true) + } +} + +impl ConfigSource for Config { + fn ignore_hidden(&self) -> Option { + self.ignore_hidden + } + + fn ignore_files(&self) -> Option { + self.ignore_files + } + + fn ignore_dot(&self) -> Option { + self.ignore_dot + } + + fn ignore_vcs(&self) -> Option { + self.ignore_vcs + } + + fn ignore_global(&self) -> Option { + self.ignore_global + } + + fn ignore_parent(&self) -> Option { + self.ignore_parent + } +} diff --git a/src/main.rs b/src/main.rs index 456a054..e15ff70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use std::io::Write; use structopt::StructOpt; +mod config; + arg_enum! { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum Format { @@ -167,8 +169,10 @@ impl Options { (_, _) => unreachable!("StructOpt should make this impossible"), } } +} - pub fn ignore_hidden(&self) -> Option { +impl config::ConfigSource for Options { + fn ignore_hidden(&self) -> Option { match (self.hidden, self.no_hidden) { (true, false) => Some(false), (false, true) => Some(true), @@ -177,49 +181,44 @@ impl Options { } } - pub fn ignore_dot(&self) -> Option { + fn ignore_files(&self) -> Option { + match (self.no_ignore, self.ignore) { + (true, false) => Some(false), + (false, true) => Some(true), + (false, false) => None, + (_, _) => unreachable!("StructOpt should make this impossible"), + } + } + + fn ignore_dot(&self) -> Option { match (self.no_ignore_dot, self.ignore_dot) { (true, false) => Some(false), (false, true) => Some(true), (false, false) => None, (_, _) => unreachable!("StructOpt should make this impossible"), } - .or_else(|| self.ignore_files()) } - pub fn ignore_global(&self) -> Option { - 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"), - } - .or_else(|| self.ignore_vcs()) - .or_else(|| self.ignore_files()) - } - - pub fn ignore_parent(&self) -> Option { - 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()) - } - - pub fn ignore_vcs(&self) -> Option { + fn ignore_vcs(&self) -> Option { 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()) } - fn ignore_files(&self) -> Option { - match (self.no_ignore, self.ignore) { + fn ignore_global(&self) -> Option { + 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"), + } + } + + fn ignore_parent(&self) -> Option { + match (self.no_ignore_parent, self.ignore_parent) { (true, false) => Some(false), (false, true) => Some(true), (false, false) => None, @@ -252,6 +251,9 @@ pub fn get_logging(level: log::Level) -> env_logger::Builder { fn run() -> Result { let options = Options::from_args().infer(); + let mut config = config::Config::default(); + config.update(&options); + let mut builder = get_logging(options.verbose.log_level()); builder.init(); @@ -280,12 +282,12 @@ fn run() -> Result { for path in &options.path[1..] { walk.add(path); } - walk.hidden(options.ignore_hidden().unwrap_or(true)) - .ignore(options.ignore_dot().unwrap_or(true)) - .git_global(options.ignore_global().unwrap_or(true)) - .git_ignore(options.ignore_vcs().unwrap_or(true)) - .git_exclude(options.ignore_vcs().unwrap_or(true)) - .parents(options.ignore_parent().unwrap_or(true)); + walk.hidden(config.ignore_hidden()) + .ignore(config.ignore_dot()) + .git_global(config.ignore_global()) + .git_ignore(config.ignore_vcs()) + .git_exclude(config.ignore_vcs()) + .parents(config.ignore_parent()); let mut typos_found = false; for entry in walk.build() { let entry = entry?;