1 use std::env::var_os;
2
3 use atty::{self, Stream};
4 use clap::{arg_enum, App, Arg};
5
6 use xd::table::*;
7
8 macro_rules! value_t_default_exit {
9 ($m:ident, $v:expr, $t:ty) => {
10 match ::clap::value_t!($m, $v, $t) {
11 Err(::clap::Error {
12 kind: ::clap::ErrorKind::ArgumentNotFound,
13 ..
14 }) => <$t as ::core::default::Default>::default(),
15 Err(other) => other.exit(),
16 Ok(other) => other,
17 }
18 };
19 }
20
21 arg_enum! {
22 #[derive(Debug)]
23 #[allow(non_camel_case_types)]
24 pub enum TableOption {
25 default,
26 classic,
27 reverse,
28 }
29 }
30
31 impl Default for TableOption {
default() -> Self32 fn default() -> Self {
33 Self::default
34 }
35 }
36
37 arg_enum! {
38 #[derive(Debug)]
39 #[allow(non_camel_case_types)]
40 pub enum ColorOption {
41 auto,
42 never,
43 always,
44 }
45 }
46
47 impl ColorOption {
should(&self) -> bool48 pub fn should(&self) -> bool {
49 match self {
50 Self::never => return false,
51 Self::always => return true,
52 Self::auto => (),
53 }
54
55 if !atty::is(Stream::Stdout) {
56 return false;
57 }
58
59 let missing_term_is_meaningful = cfg!(unix);
60
61 if var_os("TERM").map_or(missing_term_is_meaningful, |x| x == "dumb") {
62 return false;
63 }
64
65 // “preference” environment variables not yet enabled, because i’m not yet convinced that
66 // either of these conventions is accepted widely enough to commit to permanent support
67
68 #[cfg(any())]
69 if var("CLICOLOR_FORCE").as_ref().map(|x| &**x).unwrap_or("0") != "0" {
70 return true;
71 }
72
73 #[cfg(any())]
74 if let Ok("0") = var("CLICOLOR").as_ref().map(|x| &**x) {
75 return false;
76 }
77
78 #[cfg(any())]
79 if var("NO_COLOR").is_ok() {
80 return false;
81 }
82
83 return true;
84 }
85 }
86
87 impl Default for ColorOption {
default() -> Self88 fn default() -> Self {
89 Self::auto
90 }
91 }
92
93 impl From<TableOption> for &xd::table::Table {
from(variant: TableOption) -> Self94 fn from(variant: TableOption) -> Self {
95 match variant {
96 TableOption::default => &DEFAULT,
97 TableOption::classic => &CLASSIC,
98 TableOption::reverse => &REVERSE,
99 }
100 }
101 }
102
103 #[rustfmt::skip]
app() -> App<'static, 'static>104 pub fn app() -> App<'static, 'static> {
105 App::new("xd")
106 .version(clap::crate_version!())
107 .author(clap::crate_authors!("\n"))
108 .about("
109 Dumps binary input in a variety of formats.
110
111 The default format describes up to 16 octets on each line, in both hexadecimal
112 and text, and the latter with a tweaked code page 437, like on an old IBM PC.
113
114 This encoding provides a unique glyph for each octet, making it reversible,
115 and almost all of those glyphs are visually distinct, which is helpful when
116 visualising patterns in binary data that transcends printable ASCII.
117
118 Use -h for short descriptions and --help for more details.
119
120 Project home page: <https://bitbucket.org/delan/xd>
121 ")
122 .arg(Arg::from_usage("--example 'Use an example as input'").display_order(0))
123 .arg(Arg::from_usage("[color] --color [when] 'When to use colours or other text styles\n'")
124 .case_insensitive(true)
125 .possible_values(&ColorOption::variants())
126 // FIXME if only clap had a hide_possible_values_long_help
127 .hide_possible_values(true)
128 .long_help(concat!("When to use colours or other text styles:
129 • auto [default] try to be smart
130 • never never use colours or other text styles
131 • always always use colours or other text styles
132
133 In auto mode, we decide whether or not to use them as follows:
134 • no, if standard output is not a tty (Unix) or console (Windows)
135 • no, if the environment has TERM=dumb (all platforms)
136 • no, if the environment has no TERM (Unix)
137 ", /* " • yes, if the environment has CLICOLOR_FORCE≠0
138 ", " • no, if the environment has CLICOLOR=0 or NO_COLOR
139 ", */ " • yes, otherwise
140 ", /* FIXME why does clap trim this line off, when it leaves a blank line after other options? */ " ")))
141 .arg(Arg::from_usage("[table] --table [name] 'Character set to use for text columns\n'")
142 .case_insensitive(true)
143 .possible_values(&TableOption::variants())
144 // FIXME if only clap had a hide_possible_values_long_help
145 .hide_possible_values(true)
146 .long_help(concat!("Character set to use for text columns:
147 • default code page 437 (full graphics) + symbol for null
148 • classic printable ASCII + dot (.) for everything else
149 • reverse control pictures + reverse video for high octets
150 ", /* FIXME why does clap trim this line off, when it leaves a blank line after other options? */ " ")))
151 .arg(Arg::from_usage("--unbuffered 'Flush output after each line'").display_order(0).alias("line-buffered"))
152 .arg(Arg::from_usage("[input] 'Input file path [default: standard input]'"))
153 }
154