git_simple_encrypt/
cli.rs1use std::path::{Path, PathBuf};
2
3use clap::{Parser, Subcommand};
4use config_file2::StoreConfigFile;
5use log::{info, warn};
6
7use crate::{
8 config::CONFIG_FILE_NAME,
9 repo::{GitCommand, Repo},
10};
11
12#[derive(Parser, Clone, Debug)]
13#[command(author, version, about, long_about = None, after_help = r#"Examples:
14git-se p # set password
15git-se add file.txt # mark `file.txt` as need-to-be-crypted
16git-se e # encrypt current repo with all marked files
17git-se d # decrypt current repo
18git-se d 'src/*' # decrypt all encrypted files in `src` folder
19"#)]
20#[clap(args_conflicts_with_subcommands = true)]
21pub struct Cli {
22 #[command(subcommand)]
24 pub command: SubCommand,
25 #[arg(short, long, global = true)]
27 #[clap(value_parser = repo_path_parser, default_value = ".")]
28 pub repo: PathBuf,
29}
30
31fn repo_path_parser(path: &str) -> Result<PathBuf, String> {
32 match path_absolutize::Absolutize::absolutize(Path::new(path)) {
33 Ok(p) => Ok(p.into_owned()),
34 Err(e) => Err(format!("{e}")),
35 }
36}
37
38impl Default for Cli {
39 fn default() -> Self {
40 Self {
41 command: SubCommand::default(),
42 repo: PathBuf::from("."),
43 }
44 }
45}
46
47#[derive(Subcommand, Debug, Clone, Default)]
48pub enum SubCommand {
49 #[default]
51 #[clap(alias("e"))]
52 Encrypt,
53 #[clap(alias("d"))]
55 Decrypt {
56 path: Option<String>,
58 },
59 Add {
61 #[clap(required = true)]
62 paths: Vec<String>,
63 },
64 Set {
66 #[clap(subcommand)]
67 field: SetField,
68 },
69 #[clap(alias("p"))]
71 Pwd,
72}
73
74#[derive(Debug, Subcommand, Clone)]
75pub enum SetField {
76 Key { value: String },
78 ZstdLevel {
80 #[clap(value_parser = validate_zstd_level)]
81 value: u8,
82 },
83 EnableZstd {
85 #[clap(value_parser = validate_bool)]
86 value: bool,
87 },
88}
89
90impl SetField {
91 pub fn set(&self, repo: &mut Repo) -> anyhow::Result<()> {
98 match self {
99 Self::Key { value } => {
100 warn!("`set key` is deprecated, please use `pwd` or `p` instead.");
101 repo.set_config("key", value)?;
102 info!("key set to `{}`", value);
103 }
104 Self::EnableZstd { value } => {
105 repo.conf.use_zstd = *value;
106 info!("zstd compression enabled: {}", value);
107 }
108 Self::ZstdLevel { value } => {
109 repo.conf.zstd_level = *value;
110 info!("zstd compression level set to {}", value);
111 }
112 };
113 repo.conf.store(CONFIG_FILE_NAME)?;
114
115 Ok(())
116 }
117}
118
119fn validate_zstd_level(value: &str) -> Result<u8, String> {
120 let value = value
121 .parse::<u8>()
122 .map_err(|_| "value should be a number")?;
123 if (1..=22_u8).contains(&value) {
124 Ok(value)
125 } else {
126 Err("value should be 1-22".to_string())
127 }
128}
129
130fn validate_bool(value: &str) -> Result<bool, String> {
131 match value {
132 "true" | "1" => Ok(true),
133 "false" | "0" => Ok(false),
134 _ => Err("value should be `true`, `false`, `1` or `0`".into()),
135 }
136}