2017-07-22 17:06:05 +00:00
|
|
|
|
//! Parsing command-line strings into exa options.
|
|
|
|
|
//!
|
2017-07-24 07:34:50 +00:00
|
|
|
|
//! This module imports exa’s configuration types, such as `View` (the details
|
|
|
|
|
//! of displaying multiple files) and `DirAction` (what to do when encountering
|
|
|
|
|
//! a directory), and implements `deduce` methods on them so they can be
|
|
|
|
|
//! configured using command-line options.
|
|
|
|
|
//!
|
2017-07-22 17:06:05 +00:00
|
|
|
|
//!
|
|
|
|
|
//! ## Useless and overridden options
|
|
|
|
|
//!
|
|
|
|
|
//! Let’s say exa was invoked with just one argument: `exa --inode`. The
|
|
|
|
|
//! `--inode` option is used in the details view, where it adds the inode
|
|
|
|
|
//! column to the output. But because the details view is *only* activated with
|
|
|
|
|
//! the `--long` argument, adding `--inode` without it would not have any
|
|
|
|
|
//! effect.
|
|
|
|
|
//!
|
|
|
|
|
//! For a long time, exa’s philosophy was that the user should be warned
|
|
|
|
|
//! whenever they could be mistaken like this. If you tell exa to display the
|
|
|
|
|
//! inode, and it *doesn’t* display the inode, isn’t that more annoying than
|
|
|
|
|
//! having it throw an error back at you?
|
|
|
|
|
//!
|
|
|
|
|
//! However, this doesn’t take into account *configuration*. Say a user wants
|
|
|
|
|
//! to configure exa so that it lists inodes in the details view, but otherwise
|
|
|
|
|
//! functions normally. A common way to do this for command-line programs is to
|
|
|
|
|
//! define a shell alias that specifies the details they want to use every
|
|
|
|
|
//! time. For the inode column, the alias would be:
|
|
|
|
|
//!
|
|
|
|
|
//! `alias exa="exa --inode"`
|
|
|
|
|
//!
|
|
|
|
|
//! Using this alias means that although the inode column will be shown in the
|
|
|
|
|
//! details view, you’re now *only* allowed to use the details view, as any
|
|
|
|
|
//! other view type will result in an error. Oops!
|
|
|
|
|
//!
|
|
|
|
|
//! Another example is when an option is specified twice, such as `exa
|
|
|
|
|
//! --sort=Name --sort=size`. Did the user change their mind about sorting, and
|
|
|
|
|
//! accidentally specify the option twice?
|
|
|
|
|
//!
|
|
|
|
|
//! Again, exa rejected this case, throwing an error back to the user instead
|
|
|
|
|
//! of trying to guess how they want their output sorted. And again, this
|
|
|
|
|
//! doesn’t take into account aliases being used to set defaults. A user who
|
|
|
|
|
//! wants their files to be sorted case-insensitively may configure their shell
|
|
|
|
|
//! with the following:
|
|
|
|
|
//!
|
|
|
|
|
//! `alias exa="exa --sort=Name"`
|
|
|
|
|
//!
|
|
|
|
|
//! Just like the earlier example, the user now can’t use any other sort order,
|
|
|
|
|
//! because exa refuses to guess which one they meant. It’s *more* annoying to
|
|
|
|
|
//! have to go back and edit the command than if there were no error.
|
|
|
|
|
//!
|
|
|
|
|
//! Fortunately, there’s a heuristic for telling which options came from an
|
|
|
|
|
//! alias and which came from the actual command-line: aliased options are
|
|
|
|
|
//! nearer the beginning of the options array, and command-line options are
|
|
|
|
|
//! nearer the end. This means that after the options have been parsed, exa
|
|
|
|
|
//! needs to traverse them *backwards* to find the last-most-specified one.
|
|
|
|
|
//!
|
|
|
|
|
//! For example, invoking exa with `exa --sort=size` when that alias is present
|
|
|
|
|
//! would result in a full command-line of:
|
|
|
|
|
//!
|
|
|
|
|
//! `exa --sort=Name --sort=size`
|
|
|
|
|
//!
|
|
|
|
|
//! `--sort=size` should override `--sort=Name` because it’s closer to the end
|
|
|
|
|
//! of the arguments array. In fact, because there’s no way to tell where the
|
2020-10-10 18:49:46 +00:00
|
|
|
|
//! arguments came from — it’s just a heuristic — this will still work even
|
2017-07-22 17:06:05 +00:00
|
|
|
|
//! if no aliases are being used!
|
|
|
|
|
//!
|
|
|
|
|
//! Finally, this isn’t just useful when options could override each other.
|
2017-09-03 18:50:40 +00:00
|
|
|
|
//! Creating an alias `exal="exa --long --inode --header"` then invoking `exal
|
2017-07-22 17:06:05 +00:00
|
|
|
|
//! --grid --long` shouldn’t complain about `--long` being given twice when
|
|
|
|
|
//! it’s clear what the user wants.
|
|
|
|
|
|
|
|
|
|
|
2020-10-12 23:29:49 +00:00
|
|
|
|
use std::ffi::OsStr;
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::fs::dir_action::DirAction;
|
2020-10-10 18:49:46 +00:00
|
|
|
|
use crate::fs::filter::{FileFilter, GitIgnore};
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::output::{View, Mode, details, grid_details};
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
|
|
|
|
mod dir_action;
|
|
|
|
|
mod filter;
|
2020-10-10 18:49:46 +00:00
|
|
|
|
mod flags;
|
|
|
|
|
mod style;
|
2017-07-24 07:34:50 +00:00
|
|
|
|
mod view;
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
mod error;
|
|
|
|
|
pub use self::error::OptionsError;
|
|
|
|
|
|
2016-04-17 19:38:37 +00:00
|
|
|
|
mod help;
|
2017-06-23 21:50:29 +00:00
|
|
|
|
use self::help::HelpString;
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
mod parser;
|
|
|
|
|
use self::parser::MatchedFlags;
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
2017-08-26 20:19:06 +00:00
|
|
|
|
pub mod vars;
|
2017-08-26 19:48:51 +00:00
|
|
|
|
pub use self::vars::Vars;
|
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
mod version;
|
|
|
|
|
use self::version::VersionString;
|
2017-07-12 11:03:07 +00:00
|
|
|
|
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
|
|
|
|
/// These **options** represent a parsed, error-checked versions of the
|
|
|
|
|
/// user’s command-line options.
|
2017-07-05 19:16:04 +00:00
|
|
|
|
#[derive(Debug)]
|
2016-04-17 19:38:37 +00:00
|
|
|
|
pub struct Options {
|
|
|
|
|
|
|
|
|
|
/// The action to perform when encountering a directory rather than a
|
|
|
|
|
/// regular file.
|
|
|
|
|
pub dir_action: DirAction,
|
|
|
|
|
|
|
|
|
|
/// How to sort and filter files before outputting them.
|
|
|
|
|
pub filter: FileFilter,
|
|
|
|
|
|
|
|
|
|
/// The type of output to use (lines, grid, or details).
|
|
|
|
|
pub view: View,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Options {
|
|
|
|
|
|
2017-08-10 16:54:28 +00:00
|
|
|
|
/// Parse the given iterator of command-line strings into an Options
|
|
|
|
|
/// struct and a list of free filenames, using the environment variables
|
|
|
|
|
/// for extra options.
|
2016-04-17 19:38:37 +00:00
|
|
|
|
#[allow(unused_results)]
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
pub fn parse<'args, I, V>(args: I, vars: &V) -> OptionsResult<'args>
|
2020-10-12 23:29:49 +00:00
|
|
|
|
where I: IntoIterator<Item = &'args OsStr>,
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
V: Vars,
|
2020-10-10 18:49:46 +00:00
|
|
|
|
{
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::options::parser::{Matches, Strictness};
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
2017-08-26 20:19:06 +00:00
|
|
|
|
let strictness = match vars.get(vars::EXA_STRICT) {
|
2017-08-10 17:45:26 +00:00
|
|
|
|
None => Strictness::UseLastArguments,
|
|
|
|
|
Some(ref t) if t.is_empty() => Strictness::UseLastArguments,
|
2020-10-13 00:46:17 +00:00
|
|
|
|
Some(_) => Strictness::ComplainAboutRedundantArguments,
|
2017-08-10 17:45:26 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let Matches { flags, frees } = match flags::ALL_ARGS.parse(args, strictness) {
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
Ok(m) => m,
|
|
|
|
|
Err(pe) => return OptionsResult::InvalidOptions(OptionsError::Parse(pe)),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
};
|
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
if let Some(help) = HelpString::deduce(&flags) {
|
|
|
|
|
return OptionsResult::Help(help);
|
|
|
|
|
}
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
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),
|
|
|
|
|
}
|
2016-04-17 19:38:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Whether the View specified in this set of options includes a Git
|
|
|
|
|
/// status column. It’s only worth trying to discover a repository if the
|
|
|
|
|
/// results will end up being displayed.
|
|
|
|
|
pub fn should_scan_for_git(&self) -> bool {
|
2020-04-19 03:52:35 +00:00
|
|
|
|
if self.filter.git_ignore == GitIgnore::CheckAndIgnore {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-24 21:39:15 +00:00
|
|
|
|
match self.view.mode {
|
2017-07-05 20:01:01 +00:00
|
|
|
|
Mode::Details(details::Options { table: Some(ref table), .. }) |
|
2019-08-29 12:34:30 +00:00
|
|
|
|
Mode::GridDetails(grid_details::Options { details: details::Options { table: Some(ref table), .. }, .. }) => table.columns.git,
|
2016-04-17 19:38:37 +00:00
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Determines the complete set of options based on the given command-line
|
|
|
|
|
/// arguments, after they’ve been parsed.
|
2020-10-13 00:36:41 +00:00
|
|
|
|
fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
|
2017-03-31 16:09:32 +00:00
|
|
|
|
let dir_action = DirAction::deduce(matches)?;
|
|
|
|
|
let filter = FileFilter::deduce(matches)?;
|
2017-08-10 16:54:28 +00:00
|
|
|
|
let view = View::deduce(matches, vars)?;
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Ok(Self { dir_action, view, filter })
|
2016-04-17 19:38:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
/// The result of the `Options::getopts` function.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum OptionsResult<'args> {
|
|
|
|
|
|
|
|
|
|
/// The options were parsed successfully.
|
|
|
|
|
Ok(Options, Vec<&'args OsStr>),
|
|
|
|
|
|
|
|
|
|
/// There was an error parsing the arguments.
|
|
|
|
|
InvalidOptions(OptionsError),
|
|
|
|
|
|
|
|
|
|
/// One of the arguments was `--help`, so display help.
|
|
|
|
|
Help(HelpString),
|
|
|
|
|
|
|
|
|
|
/// One of the arguments was `--version`, so display the version number.
|
|
|
|
|
Version(VersionString),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-04-17 19:38:37 +00:00
|
|
|
|
#[cfg(test)]
|
2017-08-08 08:18:17 +00:00
|
|
|
|
pub mod test {
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::options::parser::{Arg, MatchedFlags};
|
2020-10-12 23:29:49 +00:00
|
|
|
|
use std::ffi::OsStr;
|
2017-08-10 16:54:28 +00:00
|
|
|
|
|
2017-08-08 08:18:17 +00:00
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
|
|
pub enum Strictnesses {
|
|
|
|
|
Last,
|
|
|
|
|
Complain,
|
|
|
|
|
Both,
|
|
|
|
|
}
|
|
|
|
|
|
Be stricter in strict mode
Now the code actually starts to use the Strictness flag that was added in the earlier commit! Well, the *code* doesn’t, but the tests do: the macros that create the test cases now have a parameter for which tests they should run. It’s usually ‘Both’ for both strict mode and default mode, but can be specified to only run in one, for when the results differ (usually when options override one another)
The downside to strict mode is that, now, *any* call to `matches.has` or `matches.get` could fail, because an option could have been specified twice, and this is the place where those are checked for. This makes the code a little less ergonomic in places, but that’s what the ? operator is for. The only place this has really had an effect is in `Classify::deduce`, which used to just return a boolean but can now fail.
In order to more thoroughly test the mode, some of the older parts of the code can now act more strict. For example, `TerminalColours::deduce` will now use the last-given option rather than searching for “colours” before “colors”.
Help and Version continue doing their own thing.
2017-08-09 08:21:29 +00:00
|
|
|
|
/// This function gets used by the other testing modules.
|
|
|
|
|
/// It can run with one or both strictness values: if told to run with
|
|
|
|
|
/// both, then both should resolve to the same result.
|
2017-08-09 12:36:06 +00:00
|
|
|
|
///
|
|
|
|
|
/// It returns a vector with one or two elements in.
|
|
|
|
|
/// These elements can then be tested with assert_eq or what have you.
|
|
|
|
|
pub fn parse_for_test<T, F>(inputs: &[&str], args: &'static [&'static Arg], strictnesses: Strictnesses, get: F) -> Vec<T>
|
2020-10-13 00:36:41 +00:00
|
|
|
|
where F: Fn(&MatchedFlags<'_>) -> T
|
2017-08-08 08:18:17 +00:00
|
|
|
|
{
|
|
|
|
|
use self::Strictnesses::*;
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::options::parser::{Args, Strictness};
|
2017-08-08 08:18:17 +00:00
|
|
|
|
|
2020-10-12 23:29:49 +00:00
|
|
|
|
let bits = inputs.into_iter().map(OsStr::new).collect::<Vec<_>>();
|
2017-10-31 05:24:31 +00:00
|
|
|
|
let mut result = Vec::new();
|
2017-08-08 08:18:17 +00:00
|
|
|
|
|
|
|
|
|
if strictnesses == Last || strictnesses == Both {
|
2020-10-12 23:29:49 +00:00
|
|
|
|
let results = Args(args).parse(bits.clone(), Strictness::UseLastArguments);
|
2017-10-31 05:24:31 +00:00
|
|
|
|
result.push(get(&results.unwrap().flags));
|
2017-08-08 08:18:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strictnesses == Complain || strictnesses == Both {
|
2020-10-12 23:29:49 +00:00
|
|
|
|
let results = Args(args).parse(bits, Strictness::ComplainAboutRedundantArguments);
|
2017-10-31 05:24:31 +00:00
|
|
|
|
result.push(get(&results.unwrap().flags));
|
2017-08-08 08:18:17 +00:00
|
|
|
|
}
|
2017-08-09 12:36:06 +00:00
|
|
|
|
|
2017-10-31 05:24:31 +00:00
|
|
|
|
result
|
2017-08-08 08:18:17 +00:00
|
|
|
|
}
|
2016-04-17 19:38:37 +00:00
|
|
|
|
}
|