json_to_usv/app/
clap.rs

1//! clap setup.
2//!
3//! clap is a crate for command line argument parsing.
4//! See https://docs.rs/clap/
5//!
6//! We prefer clap using the `command!` macro, which runs at compile time.
7//! We prefer clap using the builder pattern, which offers more capabilities.
8//!
9//! We favor our convention of doing clap setup in a file named `clap.rs`,
10//! rather than in `main.rs`, because we favor the separation of concerns.
11
12use clap::{builder::styling::Style, Arg};
13
14pub fn clap() -> crate::app::args::Args {
15    let matches = matches();
16    crate::app::args::Args {
17        test: matches.get_flag("test"),
18        log_level: crate::app::log::u8_to_log_level(matches.get_count("verbose")),
19        style: style(&matches),
20    }
21}
22
23fn matches() -> clap::ArgMatches {
24    clap::command!()
25    .name(env!("CARGO_PKG_NAME"))
26    .version(env!("CARGO_PKG_VERSION"))
27    .author(env!("CARGO_PKG_AUTHORS"))
28    .about(env!("CARGO_PKG_DESCRIPTION"))
29    .arg(Arg::new("test")
30        .long("test")
31        .help("Print test output for debugging, verifying, tracing, and the like.\nExample: --test")
32        .action(clap::ArgAction::SetTrue))
33    .arg(Arg::new("verbose")
34        .short('v')
35        .long("verbose")
36        .help("Set the verbosity level: 0=none, 1=error, 2=warn, 3=info, 4=debug, 5=trace.\nExample: --verbose …")
37        .action(clap::ArgAction::Count))
38        .arg(Arg::new("unit-separator")
39        .help("Set the unit separator (US) string")
40        .short('u')
41        .long("unit-separator")
42        .action(clap::ArgAction::Set))
43    .arg(Arg::new("record-separator")
44        .help("Set the record separator (RS) string.")
45        .short('r')
46        .long("record-separator")
47        .action(clap::ArgAction::Set))
48    .arg(Arg::new("group-separator")
49        .help("Set the group separator (GS) string.")
50        .short('g')
51        .long("group-separator")
52        .action(clap::ArgAction::Set))
53    .arg(Arg::new("file-separator")
54        .help("Set the file separator (FS) string.")
55        .short('f')
56        .long("file-separator")
57        .action(clap::ArgAction::Set))
58    .arg(Arg::new("escape")
59        .help("Set the escape (ESC) string.")
60        .short('e')
61        .long("escape")
62        .action(clap::ArgAction::Set))
63    .arg(Arg::new("end-of-transmission")
64        .help("Set the end of transmission (EOT) string.")
65        .short('z')
66        .long("end-of-transmission")
67        .action(clap::ArgAction::Set))
68    .arg(Arg::new("style-symbols")
69        .help(r#"Show marks as symbols, such as "␟" for Unit Separator."#)
70        .long("style-symbols")
71        .conflicts_with_all([
72            "style-braces",
73            "style-controls"
74        ])
75        .action(clap::ArgAction::SetTrue))
76    .arg(Arg::new("style-controls")
77        .help(r#"Show marks as controls, such as "\u001F" for Unit Separator."#)
78        .long("style-controls")
79        .conflicts_with_all([
80            "style-braces",
81            "style-symbols",
82        ])
83        .action(clap::ArgAction::SetTrue))
84    .arg(Arg::new("style-braces")
85        .help(r#"Show marks as braces, such as "{US}" for Unit Separator. This is to help plain text readers, and is not USV output."#)
86        .long("style-braces")
87        .conflicts_with_all([
88            "style-controls",
89            "style-symbols",
90        ])
91        .action(clap::ArgAction::SetTrue))
92    .arg(Arg::new("layout-units")
93        .help(r#"Show each unit on one line. This can be helpful for line-oriented tools."#)
94        .long("layout-units")
95        .conflicts_with_all([
96            "layout-records",
97            "layout-groups",
98            "layout-files",
99            "layout-0",
100            "layout-1",
101            "layout-2",
102            ])
103        .action(clap::ArgAction::SetTrue))
104        .arg(Arg::new("layout-records")
105        .help(r#"Show each record on one line. This is like a typical spreadsheet sheet export."#)
106        .long("layout-records")
107        .conflicts_with_all([
108            "layout-units",
109            "layout-groups",
110            "layout-files",
111            "layout-0",
112            "layout-1",
113            "layout-2",
114            ])
115        .action(clap::ArgAction::SetTrue))
116        .arg(Arg::new("layout-groups")
117        .help(r#"Show each group on one line. This can be helpful for folio-oriented tools."#)
118        .long("layout-groups")
119        .conflicts_with_all([
120            "layout-units",
121            "layout-records",
122            "layout-files",
123            "layout-0",
124            "layout-1",
125            "layout-2",
126            ])
127        .action(clap::ArgAction::SetTrue))
128        .arg(Arg::new("layout-files")
129        .help(r#"Show each file on one line. This can be helpful for archive-oriented tools."#)
130        .long("layout-files")
131        .conflicts_with_all([
132            "layout-units",
133            "layout-records",
134            "layout-groups",
135            "layout-0",
136            "layout-1",
137            "layout-2",
138            ])
139        .action(clap::ArgAction::SetTrue))
140        .arg(Arg::new("layout-0")
141        .help(r#"Show each item with no line around it. This is no layout, in other words one long line."#)
142        .long("layout-0")
143        .conflicts_with_all([
144            "layout-units",
145            "layout-records",
146            "layout-groups",
147            "layout-files",
148            "layout-1",
149            "layout-2",
150            ])
151        .action(clap::ArgAction::SetTrue))
152        .arg(Arg::new("layout-1")
153        .help(r#"Show each item with one line around it. This is like single-space lines for long form text."#)
154        .long("layout-1")
155        .conflicts_with_all([
156            "layout-units",
157            "layout-records",
158            "layout-groups",
159            "layout-files",
160            "layout-0",
161            "layout-2",
162            ])
163        .action(clap::ArgAction::SetTrue))
164        .arg(Arg::new("layout-2")
165        .help(r#"Show each item with two lines around it. This is like double-space lines for long form text."#)
166        .long("layout-2")
167        .conflicts_with_all([
168            "layout-units",
169            "layout-records",
170            "layout-groups",
171            "layout-files",
172            "layout-0",
173            "layout-1",
174            ])
175        .action(clap::ArgAction::SetTrue))
176    .get_matches()
177}
178
179fn style(matches: &clap::ArgMatches) -> usv::style::Style {
180    let style = if matches.get_flag("style-symbols") {
181        usv::style::style_symbols()
182    } else
183    if matches.get_flag("style-controls") {
184        usv::style::style_controls()
185    } else
186    if matches.get_flag("style-braces") {
187        usv::style::style_braces()
188    } else {
189        usv::style::style_symbols()
190    };
191    let mut style = layout(matches).map_style(&style);
192    if let Some(x) = matches.get_one::<String>("unit-separator") {
193        style.unit_separator = String::from(x)
194    }
195    if let Some(x) = matches.get_one::<String>("record-separator") {
196        style.record_separator = String::from(x)
197    }
198    if let Some(x) = matches.get_one::<String>("group-separator") {
199        style.group_separator = String::from(x)
200    }
201    if let Some(x) = matches.get_one::<String>("file-separator") {
202        style.file_separator = String::from(x)
203    }
204    if let Some(x) = matches.get_one::<String>("escape") {
205        style.escape = String::from(x)
206    }
207    if let Some(x) = matches.get_one::<String>("end-of-transmission") {
208        style.end_of_transmission = String::from(x)
209    }
210    style
211}
212
213fn layout(matches: &clap::ArgMatches) -> Box<dyn usv::style::MapStyle> {
214    if matches.get_flag("layout-0") {
215        Box::new(usv::layout::layout_0::Layout0)
216    } else
217    if matches.get_flag("layout-1") {
218        Box::new(usv::layout::layout_1::Layout1)
219    } else
220    if matches.get_flag("layout-2") {
221        Box::new(usv::layout::layout_2::Layout2)
222    } else
223    if matches.get_flag("layout-units") {
224        Box::new(usv::layout::layout_units::LayoutUnits)
225    } else
226    if matches.get_flag("layout-records") {
227        Box::new(usv::layout::layout_records::LayoutRecords)
228    } else
229    if matches.get_flag("layout-groups") {
230        Box::new(usv::layout::layout_groups::LayoutGroups)
231    } else
232    if matches.get_flag("layout-files") {
233        Box::new(usv::layout::layout_files::LayoutFiles)
234    } else {
235        Box::new(usv::layout::layout_records::LayoutRecords)
236    }
237}