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 password
12git-se add file.txt # mark `file.txt` as need-to-be-crypted
13git-se e # encrypt current repo with all marked files
14git-se d # decrypt current repo
15git-se d 'src/*' # decrypt all encrypted files in `src` folder
16"#)]
17#[clap(args_conflicts_with_subcommands = true)]
18pub struct Cli {
19 #[command(subcommand)]
21 pub command: SubCommand,
22 #[arg(short, long, global = true)]
24 #[clap(value_parser = repo_path_parser, default_value = ".")]
25 pub repo: PathBuf,
26}
27
28fn repo_path_parser(path: &str) -> Result<PathBuf, String> {
29 match path_absolutize::Absolutize::absolutize(Path::new(path)) {
30 Ok(p) => Ok(p.into_owned()),
31 Err(e) => Err(format!("{e}")),
32 }
33}
34
35impl Default for Cli {
36 fn default() -> Self {
37 Self {
38 command: SubCommand::Pwd,
39 repo: PathBuf::from("."),
40 }
41 }
42}
43
44#[derive(Subcommand, Debug, Clone)]
45pub enum SubCommand {
46 #[clap(alias("e"))]
48 Encrypt {
49 paths: Vec<PathBuf>,
51 },
52 #[clap(alias("d"))]
54 Decrypt {
55 paths: Vec<PathBuf>,
57 },
58 Add { paths: Vec<PathBuf> },
60 Set {
62 #[clap(subcommand)]
63 field: SetField,
64 },
65 #[clap(alias("p"))]
67 Pwd,
68}
69
70#[derive(Debug, Subcommand, Clone)]
71pub enum SetField {
72 Key { value: String },
74 ZstdLevel {
76 #[clap(value_parser = validate_zstd_level)]
77 value: u8,
78 },
79 EnableZstd {
81 #[clap(value_parser = validate_bool)]
82 value: bool,
83 },
84}
85
86impl SetField {
87 pub fn set(&self, repo: &mut Repo) -> anyhow::Result<()> {
94 match self {
95 Self::Key { value } => {
96 warn!("`set key` is deprecated, please use `pwd` or `p` instead.");
97 repo.set_config("key", value)?;
98 info!("key set to `{value}`");
99 }
100 Self::EnableZstd { value } => {
101 repo.conf.use_zstd = *value;
102 info!("zstd compression enabled: {value}");
103 }
104 Self::ZstdLevel { value } => {
105 repo.conf.zstd_level = *value;
106 info!("zstd compression level set to {value}");
107 }
108 }
109 debug!("store config to {}", repo.conf.config_path.display());
110 repo.conf.store()?;
111
112 Ok(())
113 }
114}
115
116fn validate_zstd_level(value: &str) -> Result<u8, String> {
117 let value = value
118 .parse::<u8>()
119 .map_err(|_| "value should be a number")?;
120 if (1..=22_u8).contains(&value) {
121 Ok(value)
122 } else {
123 Err("value should be 1-22".to_string())
124 }
125}
126
127fn validate_bool(value: &str) -> Result<bool, String> {
128 match value {
129 "true" | "1" => Ok(true),
130 "false" | "0" => Ok(false),
131 _ => Err("value should be `true`, `false`, `1` or `0`".into()),
132 }
133}