view.rs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. use crate::fs::feature::xattr;
  2. use crate::options::{flags, OptionsError, NumberSource, Vars};
  3. use crate::options::parser::MatchedFlags;
  4. use crate::output::{View, Mode, TerminalWidth, grid, details};
  5. use crate::output::grid_details::{self, RowThreshold};
  6. use crate::output::file_name::Options as FileStyle;
  7. use crate::output::table::{TimeTypes, SizeFormat, UserFormat, Columns, Options as TableOptions};
  8. use crate::output::time::TimeFormat;
  9. impl View {
  10. pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
  11. let mode = Mode::deduce(matches, vars)?;
  12. let width = TerminalWidth::deduce(vars)?;
  13. let file_style = FileStyle::deduce(matches, vars)?;
  14. Ok(Self { mode, width, file_style })
  15. }
  16. }
  17. impl Mode {
  18. /// Determine which viewing mode to use based on the user’s options.
  19. ///
  20. /// As with the other options, arguments are scanned right-to-left and the
  21. /// first flag found is matched, so `exa --oneline --long` will pick a
  22. /// details view, and `exa --long --oneline` will pick the lines view.
  23. ///
  24. /// This is complicated a little by the fact that `--grid` and `--tree`
  25. /// can also combine with `--long`, so care has to be taken to use the
  26. pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
  27. let flag = matches.has_where_any(|f| f.matches(&flags::LONG) || f.matches(&flags::ONE_LINE)
  28. || f.matches(&flags::GRID) || f.matches(&flags::TREE));
  29. let flag = if let Some(f) = flag { f } else {
  30. Self::strict_check_long_flags(matches)?;
  31. let grid = grid::Options::deduce(matches)?;
  32. return Ok(Self::Grid(grid));
  33. };
  34. if flag.matches(&flags::LONG)
  35. || (flag.matches(&flags::TREE) && matches.has(&flags::LONG)?)
  36. || (flag.matches(&flags::GRID) && matches.has(&flags::LONG)?)
  37. {
  38. let _ = matches.has(&flags::LONG)?;
  39. let details = details::Options::deduce_long(matches, vars)?;
  40. let flag = matches.has_where_any(|f| f.matches(&flags::GRID) || f.matches(&flags::TREE));
  41. if flag.is_some() && flag.unwrap().matches(&flags::GRID) {
  42. let _ = matches.has(&flags::GRID)?;
  43. let grid = grid::Options::deduce(matches)?;
  44. let row_threshold = RowThreshold::deduce(vars)?;
  45. let grid_details = grid_details::Options { grid, details, row_threshold };
  46. return Ok(Self::GridDetails(grid_details));
  47. }
  48. // the --tree case is handled by the DirAction parser later
  49. return Ok(Self::Details(details));
  50. }
  51. Self::strict_check_long_flags(matches)?;
  52. if flag.matches(&flags::TREE) {
  53. let _ = matches.has(&flags::TREE)?;
  54. let details = details::Options::deduce_tree(matches)?;
  55. return Ok(Self::Details(details));
  56. }
  57. if flag.matches(&flags::ONE_LINE) {
  58. let _ = matches.has(&flags::ONE_LINE)?;
  59. return Ok(Self::Lines);
  60. }
  61. let grid = grid::Options::deduce(matches)?;
  62. Ok(Self::Grid(grid))
  63. }
  64. fn strict_check_long_flags(matches: &MatchedFlags<'_>) -> Result<(), OptionsError> {
  65. // If --long hasn’t been passed, then check if we need to warn the
  66. // user about flags that won’t have any effect.
  67. if matches.is_strict() {
  68. for option in &[ &flags::BINARY, &flags::BYTES, &flags::INODE, &flags::LINKS,
  69. &flags::HEADER, &flags::BLOCKS, &flags::TIME, &flags::GROUP, &flags::NUMERIC ] {
  70. if matches.has(option)? {
  71. return Err(OptionsError::Useless(*option, false, &flags::LONG));
  72. }
  73. }
  74. if matches.has(&flags::GIT)? {
  75. return Err(OptionsError::Useless(&flags::GIT, false, &flags::LONG));
  76. }
  77. else if matches.has(&flags::LEVEL)? && ! matches.has(&flags::RECURSE)? && ! matches.has(&flags::TREE)? {
  78. return Err(OptionsError::Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE));
  79. }
  80. }
  81. Ok(())
  82. }
  83. }
  84. impl grid::Options {
  85. fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
  86. let grid = grid::Options {
  87. across: matches.has(&flags::ACROSS)?,
  88. };
  89. Ok(grid)
  90. }
  91. }
  92. impl details::Options {
  93. fn deduce_tree(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
  94. let details = details::Options {
  95. table: None,
  96. header: false,
  97. xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?,
  98. };
  99. Ok(details)
  100. }
  101. fn deduce_long<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
  102. if matches.is_strict() {
  103. if matches.has(&flags::ACROSS)? && ! matches.has(&flags::GRID)? {
  104. return Err(OptionsError::Useless(&flags::ACROSS, true, &flags::LONG));
  105. }
  106. else if matches.has(&flags::ONE_LINE)? {
  107. return Err(OptionsError::Useless(&flags::ONE_LINE, true, &flags::LONG));
  108. }
  109. }
  110. Ok(details::Options {
  111. table: Some(TableOptions::deduce(matches, vars)?),
  112. header: matches.has(&flags::HEADER)?,
  113. xattr: xattr::ENABLED && matches.has(&flags::EXTENDED)?,
  114. })
  115. }
  116. }
  117. impl TerminalWidth {
  118. fn deduce<V: Vars>(vars: &V) -> Result<Self, OptionsError> {
  119. use crate::options::vars;
  120. if let Some(columns) = vars.get(vars::COLUMNS).and_then(|s| s.into_string().ok()) {
  121. match columns.parse() {
  122. Ok(width) => {
  123. Ok(Self::Set(width))
  124. }
  125. Err(e) => {
  126. let source = NumberSource::Env(vars::COLUMNS);
  127. Err(OptionsError::FailedParse(columns, source, e))
  128. }
  129. }
  130. }
  131. else {
  132. Ok(Self::Automatic)
  133. }
  134. }
  135. }
  136. impl RowThreshold {
  137. fn deduce<V: Vars>(vars: &V) -> Result<Self, OptionsError> {
  138. use crate::options::vars;
  139. if let Some(columns) = vars.get(vars::EXA_GRID_ROWS).and_then(|s| s.into_string().ok()) {
  140. match columns.parse() {
  141. Ok(rows) => {
  142. Ok(Self::MinimumRows(rows))
  143. }
  144. Err(e) => {
  145. let source = NumberSource::Env(vars::EXA_GRID_ROWS);
  146. Err(OptionsError::FailedParse(columns, source, e))
  147. }
  148. }
  149. }
  150. else {
  151. Ok(Self::AlwaysGrid)
  152. }
  153. }
  154. }
  155. impl TableOptions {
  156. fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
  157. let time_format = TimeFormat::deduce(matches, vars)?;
  158. let size_format = SizeFormat::deduce(matches)?;
  159. let user_format = UserFormat::deduce(matches)?;
  160. let columns = Columns::deduce(matches)?;
  161. Ok(Self { size_format, time_format, user_format, columns })
  162. }
  163. }
  164. impl Columns {
  165. fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
  166. let time_types = TimeTypes::deduce(matches)?;
  167. let git = matches.has(&flags::GIT)?;
  168. let subdir_git_repos = matches.has(&flags::GIT_REPOS)?;
  169. let subdir_git_repos_no_stat = !subdir_git_repos && matches.has(&flags::GIT_REPOS_NO_STAT)?;
  170. let blocks = matches.has(&flags::BLOCKS)?;
  171. let group = matches.has(&flags::GROUP)?;
  172. let inode = matches.has(&flags::INODE)?;
  173. let links = matches.has(&flags::LINKS)?;
  174. let octal = matches.has(&flags::OCTAL)?;
  175. let permissions = ! matches.has(&flags::NO_PERMISSIONS)?;
  176. let filesize = ! matches.has(&flags::NO_FILESIZE)?;
  177. let user = ! matches.has(&flags::NO_USER)?;
  178. Ok(Self { time_types, inode, links, blocks, group, git, subdir_git_repos, subdir_git_repos_no_stat,octal, permissions, filesize, user })
  179. }
  180. }
  181. impl SizeFormat {
  182. /// Determine which file size to use in the file size column based on
  183. /// the user’s options.
  184. ///
  185. /// The default mode is to use the decimal prefixes, as they are the
  186. /// most commonly-understood, and don’t involve trying to parse large
  187. /// strings of digits in your head. Changing the format to anything else
  188. /// involves the `--binary` or `--bytes` flags, and these conflict with
  189. /// each other.
  190. fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
  191. let flag = matches.has_where(|f| f.matches(&flags::BINARY) || f.matches(&flags::BYTES))?;
  192. Ok(match flag {
  193. Some(f) if f.matches(&flags::BINARY) => Self::BinaryBytes,
  194. Some(f) if f.matches(&flags::BYTES) => Self::JustBytes,
  195. _ => Self::DecimalBytes,
  196. })
  197. }
  198. }
  199. impl TimeFormat {
  200. /// Determine how time should be formatted in timestamp columns.
  201. fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
  202. let word =
  203. if let Some(w) = matches.get(&flags::TIME_STYLE)? {
  204. w.to_os_string()
  205. }
  206. else {
  207. use crate::options::vars;
  208. match vars.get(vars::TIME_STYLE) {
  209. Some(ref t) if ! t.is_empty() => t.clone(),
  210. _ => return Ok(Self::DefaultFormat)
  211. }
  212. };
  213. if &word == "default" {
  214. Ok(Self::DefaultFormat)
  215. }
  216. else if &word == "iso" {
  217. Ok(Self::ISOFormat)
  218. }
  219. else if &word == "long-iso" {
  220. Ok(Self::LongISO)
  221. }
  222. else if &word == "full-iso" {
  223. Ok(Self::FullISO)
  224. }
  225. else {
  226. Err(OptionsError::BadArgument(&flags::TIME_STYLE, word))
  227. }
  228. }
  229. }
  230. impl UserFormat {
  231. fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
  232. let flag = matches.has(&flags::NUMERIC)?;
  233. Ok(if flag { Self::Numeric } else { Self::Name })
  234. }
  235. }
  236. impl TimeTypes {
  237. /// Determine which of a file’s time fields should be displayed for it
  238. /// based on the user’s options.
  239. ///
  240. /// There are two separate ways to pick which fields to show: with a
  241. /// flag (such as `--modified`) or with a parameter (such as
  242. /// `--time=modified`). An error is signaled if both ways are used.
  243. ///
  244. /// It’s valid to show more than one column by passing in more than one
  245. /// option, but passing *no* options means that the user just wants to
  246. /// see the default set.
  247. fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
  248. let possible_word = matches.get(&flags::TIME)?;
  249. let modified = matches.has(&flags::MODIFIED)?;
  250. let changed = matches.has(&flags::CHANGED)?;
  251. let accessed = matches.has(&flags::ACCESSED)?;
  252. let created = matches.has(&flags::CREATED)?;
  253. let no_time = matches.has(&flags::NO_TIME)?;
  254. let time_types = if no_time {
  255. Self { modified: false, changed: false, accessed: false, created: false }
  256. } else if let Some(word) = possible_word {
  257. if modified {
  258. return Err(OptionsError::Useless(&flags::MODIFIED, true, &flags::TIME));
  259. }
  260. else if changed {
  261. return Err(OptionsError::Useless(&flags::CHANGED, true, &flags::TIME));
  262. }
  263. else if accessed {
  264. return Err(OptionsError::Useless(&flags::ACCESSED, true, &flags::TIME));
  265. }
  266. else if created {
  267. return Err(OptionsError::Useless(&flags::CREATED, true, &flags::TIME));
  268. }
  269. else if word == "mod" || word == "modified" {
  270. Self { modified: true, changed: false, accessed: false, created: false }
  271. }
  272. else if word == "ch" || word == "changed" {
  273. Self { modified: false, changed: true, accessed: false, created: false }
  274. }
  275. else if word == "acc" || word == "accessed" {
  276. Self { modified: false, changed: false, accessed: true, created: false }
  277. }
  278. else if word == "cr" || word == "created" {
  279. Self { modified: false, changed: false, accessed: false, created: true }
  280. }
  281. else {
  282. return Err(OptionsError::BadArgument(&flags::TIME, word.into()));
  283. }
  284. }
  285. else if modified || changed || accessed || created {
  286. Self { modified, changed, accessed, created }
  287. }
  288. else {
  289. Self::default()
  290. };
  291. Ok(time_types)
  292. }
  293. }
  294. #[cfg(test)]
  295. mod test {
  296. use super::*;
  297. use std::ffi::OsString;
  298. use crate::options::flags;
  299. use crate::options::parser::{Flag, Arg};
  300. use crate::options::test::parse_for_test;
  301. use crate::options::test::Strictnesses::*;
  302. static TEST_ARGS: &[&Arg] = &[ &flags::BINARY, &flags::BYTES, &flags::TIME_STYLE,
  303. &flags::TIME, &flags::MODIFIED, &flags::CHANGED,
  304. &flags::CREATED, &flags::ACCESSED,
  305. &flags::HEADER, &flags::GROUP, &flags::INODE, &flags::GIT,
  306. &flags::LINKS, &flags::BLOCKS, &flags::LONG, &flags::LEVEL,
  307. &flags::GRID, &flags::ACROSS, &flags::ONE_LINE, &flags::TREE,
  308. &flags::NUMERIC ];
  309. macro_rules! test {
  310. ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => $result:expr) => {
  311. /// Macro that writes a test.
  312. /// If testing both strictnesses, they’ll both be done in the same function.
  313. #[test]
  314. fn $name() {
  315. for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
  316. assert_eq!(result, $result);
  317. }
  318. }
  319. };
  320. ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
  321. /// Special macro for testing Err results.
  322. /// This is needed because sometimes the Ok type doesn’t implement PartialEq.
  323. #[test]
  324. fn $name() {
  325. for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
  326. assert_eq!(result.unwrap_err(), $result);
  327. }
  328. }
  329. };
  330. ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => like $pat:pat) => {
  331. /// More general macro for testing against a pattern.
  332. /// Instead of using PartialEq, this just tests if it matches a pat.
  333. #[test]
  334. fn $name() {
  335. for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
  336. println!("Testing {:?}", result);
  337. match result {
  338. $pat => assert!(true),
  339. _ => assert!(false),
  340. }
  341. }
  342. }
  343. };
  344. ($name:ident: $type:ident <- $inputs:expr, $vars:expr; $stricts:expr => err $result:expr) => {
  345. /// Like above, but with $vars.
  346. #[test]
  347. fn $name() {
  348. for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf, &$vars)) {
  349. assert_eq!(result.unwrap_err(), $result);
  350. }
  351. }
  352. };
  353. ($name:ident: $type:ident <- $inputs:expr, $vars:expr; $stricts:expr => like $pat:pat) => {
  354. /// Like further above, but with $vars.
  355. #[test]
  356. fn $name() {
  357. for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf, &$vars)) {
  358. println!("Testing {:?}", result);
  359. match result {
  360. $pat => assert!(true),
  361. _ => assert!(false),
  362. }
  363. }
  364. }
  365. };
  366. }
  367. mod size_formats {
  368. use super::*;
  369. // Default behaviour
  370. test!(empty: SizeFormat <- []; Both => Ok(SizeFormat::DecimalBytes));
  371. // Individual flags
  372. test!(binary: SizeFormat <- ["--binary"]; Both => Ok(SizeFormat::BinaryBytes));
  373. test!(bytes: SizeFormat <- ["--bytes"]; Both => Ok(SizeFormat::JustBytes));
  374. // Overriding
  375. test!(both_1: SizeFormat <- ["--binary", "--binary"]; Last => Ok(SizeFormat::BinaryBytes));
  376. test!(both_2: SizeFormat <- ["--bytes", "--binary"]; Last => Ok(SizeFormat::BinaryBytes));
  377. test!(both_3: SizeFormat <- ["--binary", "--bytes"]; Last => Ok(SizeFormat::JustBytes));
  378. test!(both_4: SizeFormat <- ["--bytes", "--bytes"]; Last => Ok(SizeFormat::JustBytes));
  379. test!(both_5: SizeFormat <- ["--binary", "--binary"]; Complain => err OptionsError::Duplicate(Flag::Long("binary"), Flag::Long("binary")));
  380. test!(both_6: SizeFormat <- ["--bytes", "--binary"]; Complain => err OptionsError::Duplicate(Flag::Long("bytes"), Flag::Long("binary")));
  381. test!(both_7: SizeFormat <- ["--binary", "--bytes"]; Complain => err OptionsError::Duplicate(Flag::Long("binary"), Flag::Long("bytes")));
  382. test!(both_8: SizeFormat <- ["--bytes", "--bytes"]; Complain => err OptionsError::Duplicate(Flag::Long("bytes"), Flag::Long("bytes")));
  383. }
  384. mod time_formats {
  385. use super::*;
  386. // These tests use pattern matching because TimeFormat doesn’t
  387. // implement PartialEq.
  388. // Default behaviour
  389. test!(empty: TimeFormat <- [], None; Both => like Ok(TimeFormat::DefaultFormat));
  390. // Individual settings
  391. test!(default: TimeFormat <- ["--time-style=default"], None; Both => like Ok(TimeFormat::DefaultFormat));
  392. test!(iso: TimeFormat <- ["--time-style", "iso"], None; Both => like Ok(TimeFormat::ISOFormat));
  393. test!(long_iso: TimeFormat <- ["--time-style=long-iso"], None; Both => like Ok(TimeFormat::LongISO));
  394. test!(full_iso: TimeFormat <- ["--time-style", "full-iso"], None; Both => like Ok(TimeFormat::FullISO));
  395. // Overriding
  396. test!(actually: TimeFormat <- ["--time-style=default", "--time-style", "iso"], None; Last => like Ok(TimeFormat::ISOFormat));
  397. test!(actual_2: TimeFormat <- ["--time-style=default", "--time-style", "iso"], None; Complain => err OptionsError::Duplicate(Flag::Long("time-style"), Flag::Long("time-style")));
  398. test!(nevermind: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"], None; Last => like Ok(TimeFormat::FullISO));
  399. test!(nevermore: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"], None; Complain => err OptionsError::Duplicate(Flag::Long("time-style"), Flag::Long("time-style")));
  400. // Errors
  401. test!(daily: TimeFormat <- ["--time-style=24-hour"], None; Both => err OptionsError::BadArgument(&flags::TIME_STYLE, OsString::from("24-hour")));
  402. // `TIME_STYLE` environment variable is defined.
  403. // If the time-style argument is not given, `TIME_STYLE` is used.
  404. test!(use_env: TimeFormat <- [], Some("long-iso".into()); Both => like Ok(TimeFormat::LongISO));
  405. // If the time-style argument is given, `TIME_STYLE` is overriding.
  406. test!(override_env: TimeFormat <- ["--time-style=full-iso"], Some("long-iso".into()); Both => like Ok(TimeFormat::FullISO));
  407. }
  408. mod time_types {
  409. use super::*;
  410. // Default behaviour
  411. test!(empty: TimeTypes <- []; Both => Ok(TimeTypes::default()));
  412. // Modified
  413. test!(modified: TimeTypes <- ["--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
  414. test!(m: TimeTypes <- ["-m"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
  415. test!(time_mod: TimeTypes <- ["--time=modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
  416. test!(t_m: TimeTypes <- ["-tmod"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
  417. // Changed
  418. #[cfg(target_family = "unix")]
  419. test!(changed: TimeTypes <- ["--changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
  420. #[cfg(target_family = "unix")]
  421. test!(time_ch: TimeTypes <- ["--time=changed"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
  422. #[cfg(target_family = "unix")]
  423. test!(t_ch: TimeTypes <- ["-t", "ch"]; Both => Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }));
  424. // Accessed
  425. test!(acc: TimeTypes <- ["--accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
  426. test!(a: TimeTypes <- ["-u"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
  427. test!(time_acc: TimeTypes <- ["--time", "accessed"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
  428. test!(time_a: TimeTypes <- ["-t", "acc"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }));
  429. // Created
  430. test!(cr: TimeTypes <- ["--created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
  431. test!(c: TimeTypes <- ["-U"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
  432. test!(time_cr: TimeTypes <- ["--time=created"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
  433. test!(t_cr: TimeTypes <- ["-tcr"]; Both => Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }));
  434. // Multiples
  435. test!(time_uu: TimeTypes <- ["-u", "--modified"]; Both => Ok(TimeTypes { modified: true, changed: false, accessed: true, created: false }));
  436. // Errors
  437. test!(time_tea: TimeTypes <- ["--time=tea"]; Both => err OptionsError::BadArgument(&flags::TIME, OsString::from("tea")));
  438. test!(t_ea: TimeTypes <- ["-tea"]; Both => err OptionsError::BadArgument(&flags::TIME, OsString::from("ea")));
  439. // Overriding
  440. test!(overridden: TimeTypes <- ["-tcr", "-tmod"]; Last => Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }));
  441. test!(overridden_2: TimeTypes <- ["-tcr", "-tmod"]; Complain => err OptionsError::Duplicate(Flag::Short(b't'), Flag::Short(b't')));
  442. }
  443. mod views {
  444. use super::*;
  445. use crate::output::grid::Options as GridOptions;
  446. // Default
  447. test!(empty: Mode <- [], None; Both => like Ok(Mode::Grid(_)));
  448. // Grid views
  449. test!(original_g: Mode <- ["-G"], None; Both => like Ok(Mode::Grid(GridOptions { across: false, .. })));
  450. test!(grid: Mode <- ["--grid"], None; Both => like Ok(Mode::Grid(GridOptions { across: false, .. })));
  451. test!(across: Mode <- ["--across"], None; Both => like Ok(Mode::Grid(GridOptions { across: true, .. })));
  452. test!(gracross: Mode <- ["-xG"], None; Both => like Ok(Mode::Grid(GridOptions { across: true, .. })));
  453. // Lines views
  454. test!(lines: Mode <- ["--oneline"], None; Both => like Ok(Mode::Lines));
  455. test!(prima: Mode <- ["-1"], None; Both => like Ok(Mode::Lines));
  456. // Details views
  457. test!(long: Mode <- ["--long"], None; Both => like Ok(Mode::Details(_)));
  458. test!(ell: Mode <- ["-l"], None; Both => like Ok(Mode::Details(_)));
  459. // Grid-details views
  460. test!(lid: Mode <- ["--long", "--grid"], None; Both => like Ok(Mode::GridDetails(_)));
  461. test!(leg: Mode <- ["-lG"], None; Both => like Ok(Mode::GridDetails(_)));
  462. // Options that do nothing with --long
  463. test!(long_across: Mode <- ["--long", "--across"], None; Last => like Ok(Mode::Details(_)));
  464. // Options that do nothing without --long
  465. test!(just_header: Mode <- ["--header"], None; Last => like Ok(Mode::Grid(_)));
  466. test!(just_group: Mode <- ["--group"], None; Last => like Ok(Mode::Grid(_)));
  467. test!(just_inode: Mode <- ["--inode"], None; Last => like Ok(Mode::Grid(_)));
  468. test!(just_links: Mode <- ["--links"], None; Last => like Ok(Mode::Grid(_)));
  469. test!(just_blocks: Mode <- ["--blocks"], None; Last => like Ok(Mode::Grid(_)));
  470. test!(just_binary: Mode <- ["--binary"], None; Last => like Ok(Mode::Grid(_)));
  471. test!(just_bytes: Mode <- ["--bytes"], None; Last => like Ok(Mode::Grid(_)));
  472. test!(just_numeric: Mode <- ["--numeric"], None; Last => like Ok(Mode::Grid(_)));
  473. #[cfg(feature = "git")]
  474. test!(just_git: Mode <- ["--git"], None; Last => like Ok(Mode::Grid(_)));
  475. test!(just_header_2: Mode <- ["--header"], None; Complain => err OptionsError::Useless(&flags::HEADER, false, &flags::LONG));
  476. test!(just_group_2: Mode <- ["--group"], None; Complain => err OptionsError::Useless(&flags::GROUP, false, &flags::LONG));
  477. test!(just_inode_2: Mode <- ["--inode"], None; Complain => err OptionsError::Useless(&flags::INODE, false, &flags::LONG));
  478. test!(just_links_2: Mode <- ["--links"], None; Complain => err OptionsError::Useless(&flags::LINKS, false, &flags::LONG));
  479. test!(just_blocks_2: Mode <- ["--blocks"], None; Complain => err OptionsError::Useless(&flags::BLOCKS, false, &flags::LONG));
  480. test!(just_binary_2: Mode <- ["--binary"], None; Complain => err OptionsError::Useless(&flags::BINARY, false, &flags::LONG));
  481. test!(just_bytes_2: Mode <- ["--bytes"], None; Complain => err OptionsError::Useless(&flags::BYTES, false, &flags::LONG));
  482. test!(just_numeric2: Mode <- ["--numeric"], None; Complain => err OptionsError::Useless(&flags::NUMERIC, false, &flags::LONG));
  483. #[cfg(feature = "git")]
  484. test!(just_git_2: Mode <- ["--git"], None; Complain => err OptionsError::Useless(&flags::GIT, false, &flags::LONG));
  485. // Contradictions and combinations
  486. test!(lgo: Mode <- ["--long", "--grid", "--oneline"], None; Both => like Ok(Mode::Lines));
  487. test!(lgt: Mode <- ["--long", "--grid", "--tree"], None; Both => like Ok(Mode::Details(_)));
  488. test!(tgl: Mode <- ["--tree", "--grid", "--long"], None; Both => like Ok(Mode::GridDetails(_)));
  489. test!(tlg: Mode <- ["--tree", "--long", "--grid"], None; Both => like Ok(Mode::GridDetails(_)));
  490. test!(ot: Mode <- ["--oneline", "--tree"], None; Both => like Ok(Mode::Details(_)));
  491. test!(og: Mode <- ["--oneline", "--grid"], None; Both => like Ok(Mode::Grid(_)));
  492. test!(tg: Mode <- ["--tree", "--grid"], None; Both => like Ok(Mode::Grid(_)));
  493. }
  494. }