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