view.rs 27 KB

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