fb2_clean/config/
parser.rs1use super::{Config, Input, Output, Tags};
2use clap::{
3 Arg, ArgAction, ArgMatches, Command, CommandFactory, Error, FromArgMatches, Parser,
4 builder::{TypedValueParser, ValueParser},
5 error::{ContextKind, ContextValue, ErrorKind},
6 value_parser,
7};
8use std::ffi::OsStr;
9
10type Result<T> = std::result::Result<T, clap::Error>;
11
12impl Parser for Config {}
13
14impl FromArgMatches for Config {
15 fn from_arg_matches(m: &ArgMatches) -> Result<Config> {
16 Self::from_arg_matches_mut(&mut m.clone())
17 }
18
19 fn update_from_arg_matches(&mut self, m: &ArgMatches) -> Result<()> {
20 self.update_from_arg_matches_mut(&mut m.clone())
21 }
22
23 fn from_arg_matches_mut(m: &mut ArgMatches) -> Result<Config> {
24 let input = match m.remove_one::<Input>("input") {
25 Some(i) => i,
26 None => Input::new(".").unwrap(),
27 };
28 let output = match m.remove_one::<Output>("output") {
29 Some(o) => o,
30 None => Output::try_from_input(&input).unwrap(),
31 };
32
33 Ok(Config {
34 input,
35 output,
36 recursive: *m.get_one::<u8>("recursive").unwrap_or(&0),
37 tags: m
38 .remove_one::<Tags>("tags")
39 .unwrap_or_else(|| Tags::default()),
40 zip: m.get_flag("zip"),
41 unzip: m.get_flag("unzip"),
42 force: m.get_flag("force"),
43 exit_on_err: m.get_flag("exit-on-err"),
44 })
45 }
46
47 fn update_from_arg_matches_mut(&mut self, m: &mut ArgMatches) -> Result<()> {
48 *self = Self::from_arg_matches_mut(m)?;
49 Ok(())
50 }
51}
52
53impl CommandFactory for Config {
54 fn command() -> Command {
55 Command::new(env!("CARGO_PKG_NAME"))
56 .version(concat!("v", env!("CARGO_PKG_VERSION")))
57 .disable_help_flag(true)
58 .disable_version_flag(true)
59 .override_usage(concat!(env!("CARGO_PKG_NAME"), " [options]"))
60 .arg(
61 Arg::new("input")
62 .short('i')
63 .long("input")
64 .value_name("path")
65 .help("Input directory OR file")
66 .value_parser(ValueParser::new(InputParser)),
67 )
68 .arg(
69 Arg::new("output")
70 .short('o')
71 .long("output")
72 .value_name("dir")
73 .help("Output directory")
74 .value_parser(ValueParser::new(OutputParser)),
75 )
76 .arg(
77 Arg::new("recursive")
78 .short('r')
79 .long("recursive")
80 .value_name("n")
81 .help("Recursive file search [up to n]")
82 .num_args(0..=1)
83 .default_missing_value("16")
84 .value_parser(value_parser!(u8).range(1..)),
85 )
86 .arg(
87 Arg::new("tags")
88 .short('t')
89 .long("tags")
90 .value_name("n[,m...]")
91 .help("Remove tags")
92 .value_parser(ValueParser::new(TagsParser)),
93 )
94 .arg(
95 Arg::new("zip")
96 .short('z')
97 .long("zip")
98 .help("Compress fb2 to fb2.zip")
99 .action(ArgAction::SetTrue),
100 )
101 .arg(
102 Arg::new("unzip")
103 .short('Z')
104 .long("unzip")
105 .alias("no-zip")
106 .help("Uncompress fb2.zip to fb2")
107 .conflicts_with("zip")
108 .action(ArgAction::SetTrue),
109 )
110 .arg(
111 Arg::new("force")
112 .short('f')
113 .long("force")
114 .help("Force overwrite input files")
115 .action(ArgAction::SetTrue),
116 )
117 .arg(
118 Arg::new("exit-on-err")
119 .short('e')
120 .long("exit-on-err")
121 .alias("exit-on-error")
122 .help("Skip clean next files on error")
123 .action(ArgAction::SetTrue),
124 )
125 .next_help_heading("Other")
126 .arg(
127 Arg::new("version")
128 .short('V')
129 .long("version")
130 .help("Show version")
131 .action(ArgAction::Version),
132 )
133 .arg(
134 Arg::new("help")
135 .short('h')
136 .long("help")
137 .help("Show help")
138 .action(ArgAction::Help),
139 )
140 }
141
142 fn command_for_update() -> Command {
143 Self::command()
144 }
145}
146
147macro_rules! ty_parser {
148 ($parser:ident, $ty:ty, $try_from_os_str:path) => {
149 #[derive(Clone)]
150 pub(super) struct $parser;
151
152 impl TypedValueParser for $parser {
153 type Value = $ty;
154
155 fn parse_ref(
156 &self,
157 cmd: &Command,
158 arg: Option<&Arg>,
159 value: &OsStr,
160 ) -> Result<Self::Value> {
161 $try_from_os_str(value).map_err(|e| {
162 let mut err = Error::new(ErrorKind::InvalidValue).with_cmd(&cmd);
163
164 if let Some(arg) = arg {
165 err.insert(
166 ContextKind::InvalidArg,
167 ContextValue::String(arg.to_string()),
168 );
169 }
170 err.insert(
171 ContextKind::InvalidValue,
172 ContextValue::String(format!(
173 "'{}' (reason: {})",
174 value.to_string_lossy(),
175 e
176 )),
177 );
178
179 err
180 })
181 }
182 }
183 };
184}
185
186ty_parser!(InputParser, Input, Input::new);
187ty_parser!(OutputParser, Output, Output::new);
188ty_parser!(TagsParser, Tags, Tags::fallible_new);