use std::ffi::OsStr;
use crate::fs::dir_action::DirAction;
use crate::fs::filter::{FileFilter, GitIgnore};
use crate::options::stdin::FilesInput;
use crate::output::{details, grid_details, Mode, View};
use crate::theme::Options as ThemeOptions;
mod dir_action;
mod file_name;
mod filter;
mod error;
#[rustfmt::skip]
mod flags;
mod theme;
mod view;
pub use self::error::{NumberSource, OptionsError};
mod help;
use self::help::HelpString;
mod parser;
use self::parser::MatchedFlags;
pub mod vars;
pub use self::vars::Vars;
pub mod config;
pub mod stdin;
mod version;
use self::version::VersionString;
#[derive(Debug)]
pub struct Options {
pub dir_action: DirAction,
pub filter: FileFilter,
pub view: View,
pub theme: ThemeOptions,
pub stdin: FilesInput,
}
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};
#[rustfmt::skip]
let strictness = match vars.get_with_fallback(vars::EZA_STRICT, 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),
}
}
#[must_use]
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)?;
let stdin = FilesInput::deduce(matches, vars)?;
Ok(Self {
dir_action,
filter,
view,
theme,
stdin,
})
}
}
#[allow(clippy::large_enum_variant)]
#[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, Copy, Clone)]
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
}
}