1use async_std::path::PathBuf;
2use bool_ext::BoolExt;
3use std::ops::Not;
4use structopt::{clap::AppSettings, StructOpt};
5
6#[cfg(test)]
7#[path = "./cli_test.rs"]
8pub mod cli_test;
9
10#[derive(thiserror::Error, Debug)]
11pub enum Error {
12 #[error("Multiple operation modes specified")]
13 MultipleOperationModes,
14 #[error("Unknown content `{content}` of environment variable `{var_name}`")]
15 UnknownEnvVarContent { var_name: String, content: String },
16}
17
18#[derive(Debug, StructOpt)]
19#[structopt(setting(AppSettings::ColoredHelp))]
20pub struct Cli {
21 #[structopt(short = "n", long)]
23 pub dry_run: bool,
24 #[structopt(short, long)]
27 pub run: bool,
28
29 #[structopt(short, long, parse(from_occurrences))]
31 pub verbose: u8,
32
33 #[structopt(short, long)]
34 pub continue_on_error: bool,
35
36 #[structopt(short = "T", long)]
40 pub traverse_tree: bool,
41
42 #[structopt(short, long)]
44 pub file: bool,
45
46 #[structopt(short, long)]
48 pub directory: bool,
49
50 #[structopt(short, long)]
52 pub symlink: bool,
53
54 #[structopt(short = "i", long)]
55 pub case_insensetive: bool,
56
57 #[structopt(
58 long,
59 default_value = "true",
60 env = "FRS_SHOW_ICONS",
61 parse(try_from_str)
62 )]
63 pub icons: bool,
64
65 pub search_pattern: String,
66 pub replace_pattern: String,
67
68 #[structopt(default_value = ".")]
69 pub base_path: PathBuf,
70}
71
72impl Cli {
73 pub fn post_automations(&mut self) -> Result<(), Error> {
75 self.set_operation_mode()?;
76 self.set_verbosity();
77 self.set_types();
78 Ok(())
79 }
80
81 fn set_operation_mode(&mut self) -> Result<(), Error> {
83 (self.run && self.dry_run)
84 .not()
85 .err(Error::MultipleOperationModes)?;
86
87 let do_var_name = "FRS_DEFAULT_OP";
88 match std::env::var(do_var_name)
89 .unwrap_or_else(|_| "DRY-RUN".to_string())
90 .to_uppercase()
91 .as_str()
92 {
93 "RUN" => {
94 if !self.dry_run {
95 self.run = true
96 }
97 }
98 "DRY-RUN" => {
99 if !self.run {
100 self.dry_run = true
101 }
102 }
103 invalid_do => {
104 return Err(Error::UnknownEnvVarContent {
105 var_name: do_var_name.to_string(),
106 content: invalid_do.to_string(),
107 })
108 }
109 }
110 Ok(())
111 }
112
113 fn set_verbosity(&mut self) {
115 self.verbose = self.verbose.max(self.dry_run as u8);
116 }
117
118 fn set_types(&mut self) {
120 let no_type_selected = !(self.file || self.directory || self.symlink);
121 self.file |= no_type_selected;
122 self.directory |= no_type_selected && !self.traverse_tree;
123 self.symlink |= no_type_selected;
124 }
125}