use std::ffi::OsStr;
use crate::fs::dir_action::DirAction;
use crate::fs::filter::{FileFilter, GitIgnore};
use crate::output::{View, Mode, details, grid_details};
use crate::theme::Options as ThemeOptions;
mod dir_action;
mod file_name;
mod filter;
mod flags;
mod theme;
mod view;
mod error;
pub use self::error::{OptionsError, NumberSource};
mod help;
use self::help::HelpString;
mod parser;
use self::parser::MatchedFlags;
pub mod vars;
pub use self::vars::Vars;
mod version;
use self::version::VersionString;
#[derive(Debug)]
pub struct Options {
pub dir_action: DirAction,
pub filter: FileFilter,
pub view: View,
pub theme: ThemeOptions,
}
impl Options {
#[allow(unused_results)]
pub fn parse<'args, I, V>(args: I, vars: &V) -> OptionsResult<'args>
where I: IntoIterator<Item = &'args OsStr>,
V: Vars,
{
use crate::options::parser::{Matches, Strictness};
let strictness = match vars.get(vars::EXA_STRICT) {
None => Strictness::UseLastArguments,
Some(ref t) if t.is_empty() => Strictness::UseLastArguments,
Some(_) => Strictness::ComplainAboutRedundantArguments,
};
let Matches { flags, frees } = match flags::ALL_ARGS.parse(args, strictness) {
Ok(m) => m,
Err(pe) => return OptionsResult::InvalidOptions(OptionsError::Parse(pe)),
};
if let Some(help) = HelpString::deduce(&flags) {
return OptionsResult::Help(help);
}
if let Some(version) = VersionString::deduce(&flags) {
return OptionsResult::Version(version);
}
match Self::deduce(&flags, vars) {
Ok(options) => OptionsResult::Ok(options, frees),
Err(oe) => OptionsResult::InvalidOptions(oe),
}
}
pub fn should_scan_for_git(&self) -> bool {
if self.filter.git_ignore == GitIgnore::CheckAndIgnore {
return true;
}
match self.view.mode {
Mode::Details(details::Options { table: Some(ref table), .. }) |
Mode::GridDetails(grid_details::Options { details: details::Options { table: Some(ref table), .. }, .. }) => table.columns.git,
_ => false,
}
}
fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
if cfg!(not(feature = "git")) &&
matches.has_where_any(|f| f.matches(&flags::GIT) || f.matches(&flags::GIT_IGNORE)).is_some() {
return Err(OptionsError::Unsupported(String::from(
"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
)));
}
let view = View::deduce(matches, vars)?;
let dir_action = DirAction::deduce(matches, matches!(view.mode, Mode::Details(_)))?;
let filter = FileFilter::deduce(matches)?;
let theme = ThemeOptions::deduce(matches, vars)?;
Ok(Self { dir_action, filter, view, theme })
}
}
#[derive(Debug)]
pub enum OptionsResult<'args> {
Ok(Options, Vec<&'args OsStr>),
InvalidOptions(OptionsError),
Help(HelpString),
Version(VersionString),
}
#[cfg(test)]
pub mod test {
use crate::options::parser::{Arg, MatchedFlags};
use std::ffi::OsStr;
#[derive(PartialEq, Eq, Debug)]
pub enum Strictnesses {
Last,
Complain,
Both,
}
pub fn parse_for_test<T, F>(inputs: &[&str], args: &'static [&'static Arg], strictnesses: Strictnesses, get: F) -> Vec<T>
where F: Fn(&MatchedFlags<'_>) -> T
{
use self::Strictnesses::*;
use crate::options::parser::{Args, Strictness};
let bits = inputs.iter().map(OsStr::new).collect::<Vec<_>>();
let mut result = Vec::new();
if strictnesses == Last || strictnesses == Both {
let results = Args(args).parse(bits.clone(), Strictness::UseLastArguments);
result.push(get(&results.unwrap().flags));
}
if strictnesses == Complain || strictnesses == Both {
let results = Args(args).parse(bits, Strictness::ComplainAboutRedundantArguments);
result.push(get(&results.unwrap().flags));
}
result
}
}