git_simple_encrypt/
cli.rs1use std::path::{Path, PathBuf};
2
3use clap::{Parser, Subcommand};
4use config_file2::Storable;
5use log::{debug, info, warn};
6
7use crate::repo::{GitCommand, Repo};
8
9#[derive(Parser, Clone, Debug)]
10#[command(author, version, about, long_about = None, after_help = r#"Examples:
11git-se p # Set/update master password
12git-se add file.txt mydir # Add files/folders to the encryption list
13git-se e # Encrypt all files in the list
14git-se d # Decrypt all files in the list
15git-se e xxx.txt dir1 ... # Encrypt specific files
16git-se d xxx.txt dir1 ... # Decrypt specific files
17git-se i # Install a pre-commit hook to check encryption before committing
18"#)]
19#[clap(args_conflicts_with_subcommands = true)]
20pub struct Cli {
21 #[command(subcommand)]
23 pub command: SubCommand,
24 #[arg(short, long, global = true)]
26 #[clap(value_parser = repo_path_parser, default_value = ".")]
27 pub repo: PathBuf,
28}
29
30fn repo_path_parser(path: &str) -> Result<PathBuf, String> {
31 match path_absolutize::Absolutize::absolutize(Path::new(path)) {
32 Ok(p) => Ok(p.into_owned()),
33 Err(e) => Err(format!("{e}")),
34 }
35}
36
37impl Default for Cli {
38 fn default() -> Self {
39 Self {
40 command: SubCommand::Pwd,
41 repo: PathBuf::from("."),
42 }
43 }
44}
45
46#[derive(Subcommand, Debug, Clone)]
47pub enum SubCommand {
48 #[clap(alias("e"))]
50 Encrypt {
51 paths: Vec<PathBuf>,
53 },
54 #[clap(alias("d"))]
56 Decrypt {
57 paths: Vec<PathBuf>,
59 },
60 Add { paths: Vec<PathBuf> },
62 Set {
64 #[clap(subcommand)]
65 field: SetField,
66 },
67 #[clap(alias("p"))]
69 Pwd,
70 #[clap(alias("c"))]
72 Check {
73 paths: Vec<PathBuf>,
76 },
77 #[clap(alias("i"))]
79 Install,
80}
81
82#[derive(Debug, Subcommand, Clone)]
83pub enum SetField {
84 Key { value: String },
86 ZstdLevel {
88 #[clap(value_parser = validate_zstd_level)]
89 value: u8,
90 },
91 EnableZstd {
93 #[clap(value_parser = validate_bool)]
94 value: bool,
95 },
96}
97
98impl SetField {
99 pub fn set(&self, repo: &mut Repo) -> anyhow::Result<()> {
106 match self {
107 Self::Key { value } => {
108 warn!("`set key` is deprecated, please use `pwd` or `p` instead.");
109 repo.set_config("key", value)?;
110 info!("key set to `{value}`");
111 }
112 Self::EnableZstd { value } => {
113 repo.conf.use_zstd = *value;
114 info!("zstd compression enabled: {value}");
115 }
116 Self::ZstdLevel { value } => {
117 repo.conf.zstd_level = *value;
118 info!("zstd compression level set to {value}");
119 }
120 }
121 debug!("store config to {}", repo.conf.config_path.display());
122 repo.conf.store()?;
123
124 Ok(())
125 }
126}
127
128fn validate_zstd_level(value: &str) -> Result<u8, String> {
129 let value = value
130 .parse::<u8>()
131 .map_err(|_| "value should be a number")?;
132 if (1..=22_u8).contains(&value) {
133 Ok(value)
134 } else {
135 Err("value should be 1-22".to_string())
136 }
137}
138
139fn validate_bool(value: &str) -> Result<bool, String> {
140 match value {
141 "true" | "1" => Ok(true),
142 "false" | "0" => Ok(false),
143 _ => Err("value should be `true`, `false`, `1` or `0`".into()),
144 }
145}