| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- use crate::fs::feature::xattr;
- use crate::options::parser::MatchedFlags;
- use crate::options::{flags, NumberSource, OptionsError, Vars};
- use crate::output::file_name::Options as FileStyle;
- use crate::output::grid_details::{self, RowThreshold};
- use crate::output::table::{
- Columns, GroupFormat, Options as TableOptions, SizeFormat, TimeTypes, UserFormat,
- };
- use crate::output::time::TimeFormat;
- use crate::output::{details, grid, Mode, TerminalWidth, View};
- impl View {
- pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
- let mode = Mode::deduce(matches, vars)?;
- let deref_links = matches.has(&flags::DEREF_LINKS)?;
- let total_size = matches.has(&flags::TOTAL_SIZE)?;
- let width = TerminalWidth::deduce(matches, vars)?;
- let file_style = FileStyle::deduce(matches, vars, width.actual_terminal_width().is_some())?;
- Ok(Self {
- mode,
- width,
- file_style,
- deref_links,
- total_size,
- })
- }
- }
- impl Mode {
- /// Determine which viewing mode to use based on the user’s options.
- ///
- /// As with the other options, arguments are scanned right-to-left and the
- /// first flag found is matched, so `exa --oneline --long` will pick a
- /// details view, and `exa --long --oneline` will pick the lines view.
- ///
- /// This is complicated a little by the fact that `--grid` and `--tree`
- /// can also combine with `--long`, so care has to be taken to use the
- pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
- let flag = matches.has_where_any(|f| {
- f.matches(&flags::LONG)
- || f.matches(&flags::ONE_LINE)
- || f.matches(&flags::GRID)
- || f.matches(&flags::TREE)
- });
- let Some(flag) = flag else {
- Self::strict_check_long_flags(matches)?;
- let grid = grid::Options::deduce(matches)?;
- return Ok(Self::Grid(grid));
- };
- if flag.matches(&flags::LONG)
- || (flag.matches(&flags::TREE) && matches.has(&flags::LONG)?)
- || (flag.matches(&flags::GRID) && matches.has(&flags::LONG)?)
- {
- let _ = matches.has(&flags::LONG)?;
- let details = details::Options::deduce_long(matches, vars)?;
- let flag =
- matches.has_where_any(|f| f.matches(&flags::GRID) || f.matches(&flags::TREE));
- if flag.is_some() && flag.unwrap().matches(&flags::GRID) {
- let _ = matches.has(&flags::GRID)?;
- let grid = grid::Options::deduce(matches)?;
- let row_threshold = RowThreshold::deduce(vars)?;
- let grid_details = grid_details::Options {
- grid,
- details,
- row_threshold,
- };
- return Ok(Self::GridDetails(grid_details));
- }
- // the --tree case is handled by the DirAction parser later
- return Ok(Self::Details(details));
- }
- Self::strict_check_long_flags(matches)?;
- if flag.matches(&flags::TREE) {
- let _ = matches.has(&flags::TREE)?;
- let details = details::Options::deduce_tree(matches)?;
- return Ok(Self::Details(details));
- }
- if flag.matches(&flags::ONE_LINE) {
- let _ = matches.has(&flags::ONE_LINE)?;
- return Ok(Self::Lines);
- }
- let grid = grid::Options::deduce(matches)?;
- Ok(Self::Grid(grid))
- }
- fn strict_check_long_flags(matches: &MatchedFlags<'_>) -> Result<(), OptionsError> {
- // If --long hasn’t been passed, then check if we need to warn the
- // user about flags that won’t have any effect.
- if matches.is_strict() {
- for option in &[
- &flags::BINARY,
- &flags::BYTES,
- &flags::INODE,
- &flags::LINKS,
- &flags::HEADER,
- &flags::BLOCKSIZE,
- &flags::TIME,
- &flags::GROUP,
- &flags::NUMERIC,
- &flags::MOUNTS,
- ] {
- if matches.has(option)? {
- return Err(OptionsError::Useless(option, false, &flags::LONG));
- }
- }
- if matches.has(&flags::GIT)? && !matches.has(&flags::NO_GIT)? {
- return Err(OptionsError::Useless(&flags::GIT, false, &flags::LONG));
- } else if matches.has(&flags::LEVEL)?
- && !matches.has(&flags::RECURSE)?
- && !matches.has(&flags::TREE)?
- {
- return Err(OptionsError::Useless2(
- &flags::LEVEL,
- &flags::RECURSE,
- &flags::TREE,
- ));
- }
- }
- Ok(())
- }
- }
- impl grid::Options {
- fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
- let grid = grid::Options {
- across: matches.has(&flags::ACROSS)?,
- };
- Ok(grid)
- }
- }
- impl details::Options {
- fn deduce_tree(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
- let details = details::Options {
- table: None,
- header: false,
- xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?,
- secattr: xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?,
- mounts: matches.has(&flags::MOUNTS)?,
- };
- Ok(details)
- }
- fn deduce_long<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
- if matches.is_strict() {
- if matches.has(&flags::ACROSS)? && !matches.has(&flags::GRID)? {
- return Err(OptionsError::Useless(&flags::ACROSS, true, &flags::LONG));
- } else if matches.has(&flags::ONE_LINE)? {
- return Err(OptionsError::Useless(&flags::ONE_LINE, true, &flags::LONG));
- }
- }
- Ok(details::Options {
- table: Some(TableOptions::deduce(matches, vars)?),
- header: matches.has(&flags::HEADER)?,
- xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?,
- secattr: xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?,
- mounts: matches.has(&flags::MOUNTS)?,
- })
- }
- }
- impl TerminalWidth {
- fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
- use crate::options::vars;
- if let Some(width) = matches.get(&flags::WIDTH)? {
- let arg_str = width.to_string_lossy();
- match arg_str.parse() {
- Ok(w) => {
- if w >= 1 {
- Ok(Self::Set(w))
- } else {
- Ok(Self::Automatic)
- }
- }
- Err(e) => {
- let source = NumberSource::Arg(&flags::WIDTH);
- Err(OptionsError::FailedParse(arg_str.to_string(), source, e))
- }
- }
- } else if let Some(columns) = vars.get(vars::COLUMNS).and_then(|s| s.into_string().ok()) {
- match columns.parse() {
- Ok(width) => Ok(Self::Set(width)),
- Err(e) => {
- let source = NumberSource::Env(vars::COLUMNS);
- Err(OptionsError::FailedParse(columns, source, e))
- }
- }
- } else {
- Ok(Self::Automatic)
- }
- }
- }
- impl RowThreshold {
- fn deduce<V: Vars>(vars: &V) -> Result<Self, OptionsError> {
- use crate::options::vars;
- if let Some(columns) = vars
- .get_with_fallback(vars::EZA_GRID_ROWS, vars::EXA_GRID_ROWS)
- .and_then(|s| s.into_string().ok())
- {
- match columns.parse() {
- Ok(rows) => Ok(Self::MinimumRows(rows)),
- Err(e) => {
- let source = NumberSource::Env(
- vars.source(vars::EZA_GRID_ROWS, vars::EXA_GRID_ROWS)
- .unwrap(),
- );
- Err(OptionsError::FailedParse(columns, source, e))
- }
- }
- } else {
- Ok(Self::AlwaysGrid)
- }
- }
- }
- impl TableOptions {
- fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
- let time_format = TimeFormat::deduce(matches, vars)?;
- let size_format = SizeFormat::deduce(matches)?;
- let user_format = UserFormat::deduce(matches)?;
- let group_format = GroupFormat::deduce(matches)?;
- let columns = Columns::deduce(matches, vars)?;
- Ok(Self {
- size_format,
- time_format,
- user_format,
- group_format,
- columns,
- })
- }
- }
- impl Columns {
- fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
- use crate::options::vars;
- let time_types = TimeTypes::deduce(matches)?;
- let no_git_env = vars
- .get_with_fallback(vars::EXA_OVERRIDE_GIT, vars::EZA_OVERRIDE_GIT)
- .is_some();
- let git = matches.has(&flags::GIT)? && !matches.has(&flags::NO_GIT)? && !no_git_env;
- let subdir_git_repos =
- matches.has(&flags::GIT_REPOS)? && !matches.has(&flags::NO_GIT)? && !no_git_env;
- let subdir_git_repos_no_stat = !subdir_git_repos
- && matches.has(&flags::GIT_REPOS_NO_STAT)?
- && !matches.has(&flags::NO_GIT)?
- && !no_git_env;
- let blocksize = matches.has(&flags::BLOCKSIZE)?;
- let group = matches.has(&flags::GROUP)?;
- let inode = matches.has(&flags::INODE)?;
- let links = matches.has(&flags::LINKS)?;
- let octal = matches.has(&flags::OCTAL)?;
- let security_context = xattr::ENABLED && matches.has(&flags::SECURITY_CONTEXT)?;
- let permissions = !matches.has(&flags::NO_PERMISSIONS)?;
- let filesize = !matches.has(&flags::NO_FILESIZE)?;
- let user = !matches.has(&flags::NO_USER)?;
- Ok(Self {
- time_types,
- inode,
- links,
- blocksize,
- group,
- git,
- subdir_git_repos,
- subdir_git_repos_no_stat,
- octal,
- security_context,
- permissions,
- filesize,
- user,
- })
- }
- }
- impl SizeFormat {
- /// Determine which file size to use in the file size column based on
- /// the user’s options.
- ///
- /// The default mode is to use the decimal prefixes, as they are the
- /// most commonly-understood, and don’t involve trying to parse large
- /// strings of digits in your head. Changing the format to anything else
- /// involves the `--binary` or `--bytes` flags, and these conflict with
- /// each other.
- fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
- let flag = matches.has_where(|f| f.matches(&flags::BINARY) || f.matches(&flags::BYTES))?;
- Ok(match flag {
- Some(f) if f.matches(&flags::BINARY) => Self::BinaryBytes,
- Some(f) if f.matches(&flags::BYTES) => Self::JustBytes,
- _ => Self::DecimalBytes,
- })
- }
- }
- impl TimeFormat {
- /// Determine how time should be formatted in timestamp columns.
- fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
- let word = if let Some(w) = matches.get(&flags::TIME_STYLE)? {
- w.to_os_string()
- } else {
- use crate::options::vars;
- match vars.get(vars::TIME_STYLE) {
- Some(ref t) if !t.is_empty() => t.clone(),
- _ => return Ok(Self::DefaultFormat),
- }
- };
- match word.to_string_lossy().as_ref() {
- "default" => Ok(Self::DefaultFormat),
- "relative" => Ok(Self::Relative),
- "iso" => Ok(Self::ISOFormat),
- "long-iso" => Ok(Self::LongISO),
- "full-iso" => Ok(Self::FullISO),
- fmt if fmt.starts_with('+') => Ok(Self::Custom {
- fmt: fmt[1..].to_owned(),
- }),
- _ => Err(OptionsError::BadArgument(&flags::TIME_STYLE, word)),
- }
- }
- }
- impl UserFormat {
- fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
- let flag = matches.has(&flags::NUMERIC)?;
- Ok(if flag { Self::Numeric } else { Self::Name })
- }
- }
- impl GroupFormat {
- fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
- let flag = matches.has(&flags::SMART_GROUP)?;
- Ok(if flag { Self::Smart } else { Self::Regular })
- }
- }
- impl TimeTypes {
- /// Determine which of a file’s time fields should be displayed for it
- /// based on the user’s options.
- ///
- /// There are two separate ways to pick which fields to show: with a
- /// flag (such as `--modified`) or with a parameter (such as
- /// `--time=modified`). An error is signaled if both ways are used.
- ///
- /// It’s valid to show more than one column by passing in more than one
- /// option, but passing *no* options means that the user just wants to
- /// see the default set.
- fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
- let possible_word = matches.get(&flags::TIME)?;
- let modified = matches.has(&flags::MODIFIED)?;
- let changed = matches.has(&flags::CHANGED)?;
- let accessed = matches.has(&flags::ACCESSED)?;
- let created = matches.has(&flags::CREATED)?;
- let no_time = matches.has(&flags::NO_TIME)?;
- #[rustfmt::skip]
- let time_types = if no_time {
- Self {
- modified: false,
- changed: false,
- accessed: false,
- created: false,
- }
- } else if let Some(word) = possible_word {
- if modified {
- return Err(OptionsError::Useless(&flags::MODIFIED, true, &flags::TIME));
- } else if changed {
- return Err(OptionsError::Useless(&flags::CHANGED, true, &flags::TIME));
- } else if accessed {
- return Err(OptionsError::Useless(&flags::ACCESSED, true, &flags::TIME));
- } else if created {
- return Err(OptionsError::Useless(&flags::CREATED, true, &flags::TIME));
- } else if word == "mod" || word == "modified" {
- Self { modified: true, changed: false, accessed: false, created: false }
- } else if word == "ch" || word == "changed" {
- Self { modified: false, changed: true, accessed: false, created: false }
- } else if word == "acc" || word == "accessed" {
- Self { modified: false, changed: false, accessed: true, created: false }
- } else if word == "cr" || word == "created" {
- Self { modified: false, changed: false, accessed: false, created: true }
- } else {
- return Err(OptionsError::BadArgument(&flags::TIME, word.into()));
- }
- } else if modified || changed || accessed || created {
- Self {
- modified,
- changed,
- accessed,
- created,
- }
- } else {
- Self::default()
- };
- Ok(time_types)
- }
- }
- #[cfg(test)]
- mod test {
- use super::*;
- use crate::options::flags;
- use crate::options::parser::{Arg, Flag};
- use std::ffi::OsString;
- use crate::options::test::parse_for_test;
- use crate::options::test::Strictnesses::*;
- static TEST_ARGS: &[&Arg] = &[
- &flags::BINARY,
- &flags::BYTES,
- &flags::TIME_STYLE,
- &flags::TIME,
- &flags::MODIFIED,
- &flags::CHANGED,
- &flags::CREATED,
- &flags::ACCESSED,
- &flags::HEADER,
- &flags::GROUP,
- &flags::INODE,
- &flags::GIT,
- &flags::LINKS,
- &flags::BLOCKSIZE,
- &flags::LONG,
- &flags::LEVEL,
- &flags::GRID,
- &flags::ACROSS,
- &flags::ONE_LINE,
- &flags::TREE,
- &flags::NUMERIC,
- ];
- #[allow(unused_macro_rules)]
- macro_rules! test {
- ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => $result:expr) => {
- /// Macro that writes a test.
- /// If testing both strictnesses, they’ll both be done in the same function.
- #[test]
- fn $name() {
- for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| {
- $type::deduce(mf)
- }) {
- assert_eq!(result, $result);
- }
- }
- };
- ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
- /// Special macro for testing Err results.
- /// This is needed because sometimes the Ok type doesn’t implement `PartialEq`.
- #[test]
- fn $name() {
- for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| {
- $type::deduce(mf)
- }) {
- assert_eq!(result.unwrap_err(), $result);
- }
- }
- };
- ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => like $pat:pat) => {
- /// More general macro for testing against a pattern.
- /// Instead of using `PartialEq`, this just tests if it matches a pat.
- #[test]
- fn $name() {
- for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| {
- $type::deduce(mf)
- }) {
- println!("Testing {:?}", result);
- match result {
- $pat => assert!(true),
- _ => assert!(false),
- }
- }
- }
- };
- ($name:ident: $type:ident <- $inputs:expr, $vars:expr; $stricts:expr => err $result:expr) => {
- /// Like above, but with $vars.
- #[test]
- fn $name() {
- for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| {
- $type::deduce(mf, &$vars)
- }) {
- assert_eq!(result.unwrap_err(), $result);
- }
- }
- };
- ($name:ident: $type:ident <- $inputs:expr, $vars:expr; $stricts:expr => like $pat:pat) => {
- /// Like further above, but with $vars.
- #[test]
- fn $name() {
- for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| {
- $type::deduce(mf, &$vars)
- }) {
- println!("Testing {:?}", result);
- match result {
- $pat => assert!(true),
- _ => assert!(false),
- }
- }
- }
- };
- }
- mod size_formats {
- use super::*;
- // Default behaviour
- test!(empty: SizeFormat <- []; Both => Ok(SizeFormat::DecimalBytes));
- // Individual flags
- test!(binary: SizeFormat <- ["--binary"]; Both => Ok(SizeFormat::BinaryBytes));
- test!(bytes: SizeFormat <- ["--bytes"]; Both => Ok(SizeFormat::JustBytes));
- // Overriding
- test!(both_1: SizeFormat <- ["--binary", "--binary"]; Last => Ok(SizeFormat::BinaryBytes));
- test!(both_2: SizeFormat <- ["--bytes", "--binary"]; Last => Ok(SizeFormat::BinaryBytes));
- test!(both_3: SizeFormat <- ["--binary", "--bytes"]; Last => Ok(SizeFormat::JustBytes));
- test!(both_4: SizeFormat <- ["--bytes", "--bytes"]; Last => Ok(SizeFormat::JustBytes));
- test!(both_5: SizeFormat <- ["--binary", "--binary"]; Complain => err OptionsError::Duplicate(Flag::Long("binary"), Flag::Long("binary")));
- test!(both_6: SizeFormat <- ["--bytes", "--binary"]; Complain => err OptionsError::Duplicate(Flag::Long("bytes"), Flag::Long("binary")));
- test!(both_7: SizeFormat <- ["--binary", "--bytes"]; Complain => err OptionsError::Duplicate(Flag::Long("binary"), Flag::Long("bytes")));
- test!(both_8: SizeFormat <- ["--bytes", "--bytes"]; Complain => err OptionsError::Duplicate(Flag::Long("bytes"), Flag::Long("bytes")));
- }
- mod time_formats {
- use super::*;
- // These tests use pattern matching because TimeFormat doesn’t
- // implement PartialEq.
- // Default behaviour
- test!(empty: TimeFormat <- [], None; Both => like Ok(TimeFormat::DefaultFormat));
- // Individual settings
- test!(default: TimeFormat <- ["--time-style=default"], None; Both => like Ok(TimeFormat::DefaultFormat));
- test!(iso: TimeFormat <- ["--time-style", "iso"], None; Both => like Ok(TimeFormat::ISOFormat));
- test!(relative: TimeFormat <- ["--time-style", "relative"], None; Both => like Ok(TimeFormat::Relative));
- test!(long_iso: TimeFormat <- ["--time-style=long-iso"], None; Both => like Ok(TimeFormat::LongISO));
- test!(full_iso: TimeFormat <- ["--time-style", "full-iso"], None; Both => like Ok(TimeFormat::FullISO));
- test!(custom_style: TimeFormat <- ["--time-style", "+%Y/%m/%d"], None; Both => like Ok(TimeFormat::Custom { .. }));
- test!(bad_custom_style: TimeFormat <- ["--time-style", "%Y/%m/%d"], None; Both => err OptionsError::BadArgument(&flags::TIME_STYLE, OsString::from("%Y/%m/%d")));
- // Overriding
- test!(actually: TimeFormat <- ["--time-style=default", "--time-style", "iso"], None; Last => like Ok(TimeFormat::ISOFormat));
- test!(actual_2: TimeFormat <- ["--time-style=default", "--time-style", "iso"], None; Complain => err OptionsError::Duplicate(Flag::Long("time-style"), Flag::Long("time-style")));
- test!(nevermind: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"], None; Last => like Ok(TimeFormat::FullISO));
- test!(nevermore: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"], None; Complain => err OptionsError::Duplicate(Flag::Long("time-style"), Flag::Long("time-style")));
- // Errors
- test!(daily: TimeFormat <- ["--time-style=24-hour"], None; Both => err OptionsError::BadArgument(&flags::TIME_STYLE, OsString::from("24-hour")));
- // `TIME_STYLE` environment variable is defined.
- // If the time-style argument is not given, `TIME_STYLE` is used.
- test!(use_env: TimeFormat <- [], Some("long-iso".into()); Both => like Ok(TimeFormat::LongISO));
- // If the time-style argument is given, `TIME_STYLE` is overriding.
- test!(override_env: TimeFormat <- ["--time-style=full-iso"], Some("long-iso".into()); Both => like Ok(TimeFormat::FullISO));
- }
- mod time_types {
- use super::*;
- // Default behaviour
- test!(empty: TimeTypes <- []; Both => Ok(TimeTypes::default()));
- // Modified
- test!(modified: TimeTypes <- ["--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
- test!(m: TimeTypes <- ["-m"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
- test!(time_mod: TimeTypes <- ["--time=modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
- test!(t_m: TimeTypes <- ["-tmod"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
- // Changed
- #[cfg(target_family = "unix")]
- test!(changed: TimeTypes <- ["--changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
- #[cfg(target_family = "unix")]
- test!(time_ch: TimeTypes <- ["--time=changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
- #[cfg(target_family = "unix")]
- test!(t_ch: TimeTypes <- ["-t", "ch"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
- // Accessed
- test!(acc: TimeTypes <- ["--accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
- test!(a: TimeTypes <- ["-u"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
- test!(time_acc: TimeTypes <- ["--time", "accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
- test!(time_a: TimeTypes <- ["-t", "acc"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
- // Created
- test!(cr: TimeTypes <- ["--created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
- test!(c: TimeTypes <- ["-U"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
- test!(time_cr: TimeTypes <- ["--time=created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
- test!(t_cr: TimeTypes <- ["-tcr"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
- // Multiples
- test!(time_uu: TimeTypes <- ["-u", "--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: true, created: false }));
- // Errors
- test!(time_tea: TimeTypes <- ["--time=tea"]; Both => err OptionsError::BadArgument(&flags::TIME, OsString::from("tea")));
- test!(t_ea: TimeTypes <- ["-tea"]; Both => err OptionsError::BadArgument(&flags::TIME, OsString::from("ea")));
- // Overriding
- test!(overridden: TimeTypes <- ["-tcr", "-tmod"]; Last => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
- test!(overridden_2: TimeTypes <- ["-tcr", "-tmod"]; Complain => err OptionsError::Duplicate(Flag::Short(b't'), Flag::Short(b't')));
- }
- mod views {
- use super::*;
- use crate::output::grid::Options as GridOptions;
- // Default
- test!(empty: Mode <- [], None; Both => like Ok(Mode::Grid(_)));
- // Grid views
- test!(original_g: Mode <- ["-G"], None; Both => like Ok(Mode::Grid(GridOptions { across: false, .. })));
- test!(grid: Mode <- ["--grid"], None; Both => like Ok(Mode::Grid(GridOptions { across: false, .. })));
- test!(across: Mode <- ["--across"], None; Both => like Ok(Mode::Grid(GridOptions { across: true, .. })));
- test!(gracross: Mode <- ["-xG"], None; Both => like Ok(Mode::Grid(GridOptions { across: true, .. })));
- // Lines views
- test!(lines: Mode <- ["--oneline"], None; Both => like Ok(Mode::Lines));
- test!(prima: Mode <- ["-1"], None; Both => like Ok(Mode::Lines));
- // Details views
- test!(long: Mode <- ["--long"], None; Both => like Ok(Mode::Details(_)));
- test!(ell: Mode <- ["-l"], None; Both => like Ok(Mode::Details(_)));
- // Grid-details views
- test!(lid: Mode <- ["--long", "--grid"], None; Both => like Ok(Mode::GridDetails(_)));
- test!(leg: Mode <- ["-lG"], None; Both => like Ok(Mode::GridDetails(_)));
- // Options that do nothing with --long
- test!(long_across: Mode <- ["--long", "--across"], None; Last => like Ok(Mode::Details(_)));
- // Options that do nothing without --long
- test!(just_header: Mode <- ["--header"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_group: Mode <- ["--group"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_inode: Mode <- ["--inode"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_links: Mode <- ["--links"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_blocks: Mode <- ["--blocksize"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_binary: Mode <- ["--binary"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_bytes: Mode <- ["--bytes"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_numeric: Mode <- ["--numeric"], None; Last => like Ok(Mode::Grid(_)));
- #[cfg(feature = "git")]
- test!(just_git: Mode <- ["--git"], None; Last => like Ok(Mode::Grid(_)));
- test!(just_header_2: Mode <- ["--header"], None; Complain => err OptionsError::Useless(&flags::HEADER, false, &flags::LONG));
- test!(just_group_2: Mode <- ["--group"], None; Complain => err OptionsError::Useless(&flags::GROUP, false, &flags::LONG));
- test!(just_inode_2: Mode <- ["--inode"], None; Complain => err OptionsError::Useless(&flags::INODE, false, &flags::LONG));
- test!(just_links_2: Mode <- ["--links"], None; Complain => err OptionsError::Useless(&flags::LINKS, false, &flags::LONG));
- test!(just_blocks_2: Mode <- ["--blocksize"], None; Complain => err OptionsError::Useless(&flags::BLOCKSIZE, false, &flags::LONG));
- test!(just_binary_2: Mode <- ["--binary"], None; Complain => err OptionsError::Useless(&flags::BINARY, false, &flags::LONG));
- test!(just_bytes_2: Mode <- ["--bytes"], None; Complain => err OptionsError::Useless(&flags::BYTES, false, &flags::LONG));
- test!(just_numeric2: Mode <- ["--numeric"], None; Complain => err OptionsError::Useless(&flags::NUMERIC, false, &flags::LONG));
- #[cfg(feature = "git")]
- test!(just_git_2: Mode <- ["--git"], None; Complain => err OptionsError::Useless(&flags::GIT, false, &flags::LONG));
- // Contradictions and combinations
- test!(lgo: Mode <- ["--long", "--grid", "--oneline"], None; Both => like Ok(Mode::Lines));
- test!(lgt: Mode <- ["--long", "--grid", "--tree"], None; Both => like Ok(Mode::Details(_)));
- test!(tgl: Mode <- ["--tree", "--grid", "--long"], None; Both => like Ok(Mode::GridDetails(_)));
- test!(tlg: Mode <- ["--tree", "--long", "--grid"], None; Both => like Ok(Mode::GridDetails(_)));
- test!(ot: Mode <- ["--oneline", "--tree"], None; Both => like Ok(Mode::Details(_)));
- test!(og: Mode <- ["--oneline", "--grid"], None; Both => like Ok(Mode::Grid(_)));
- test!(tg: Mode <- ["--tree", "--grid"], None; Both => like Ok(Mode::Grid(_)));
- }
- }
|