1use ::std::time::Duration;
2
3use ::clap::Parser;
4use ::parse_duration0::parse as parse_dur;
5
6use crate::common::CommandArgs;
7
8#[derive(Parser, Debug, PartialEq)]
9#[command(
10 name = "cached",
11 about = "Cache the output of a command for a given duration, running it only if there is no cache or it has expired. Stderr is only shown on first run."
12)]
13pub struct CachedArgs {
14 #[arg(value_parser = parse_dur, short = 'd', long = "duration", default_value = "15 min")]
16 pub duration: Duration,
17 #[clap(flatten)]
18 pub key: CachedKeyArgs,
19 #[arg(short = 's', long)]
21 pub no_cached_output: bool,
22 #[arg(short = 'x', long)]
24 pub exit_code: bool,
25 #[arg(short = 'v', long)]
27 pub verbose: bool,
28 #[command(subcommand)]
29 pub cmd: CommandArgs,
30}
31
32#[derive(Parser, Debug, PartialEq, Default)]
33#[command()]
34pub struct CachedKeyArgs {
35 #[arg(short = 'g', long)]
37 pub git_head: bool,
38 #[arg(short = 'b', long, conflicts_with = "git_head")]
40 pub git_base: bool,
41 #[arg(short = 'G', long, conflicts_with = "git_head")]
43 pub git_head_diff: bool,
44 #[arg(long)]
46 pub git_repo_dir: bool,
47 #[arg(short = 'p', long)]
49 pub git_pending: bool,
50 #[arg(short = 'e', long)]
52 pub env: Vec<String>,
53 #[arg(short = 't', long)]
55 pub text: Vec<String>,
56 #[arg(short = 'D', long)]
58 pub no_dir: bool,
59 #[arg(short = 'C', long)]
61 pub no_command: bool,
62 #[arg(short = 'E', long)]
64 pub no_direct_env: bool,
65}
66
67
68impl CachedArgs {
69 pub fn any_explicit_key(&self) -> bool {
70 self.key.git_head || self.key.git_head_diff || self.key.git_base || self.key.git_repo_dir ||
71 self.key.git_pending || !self.key.env.is_empty() || !self.key.text.is_empty()
72 }
73}
74
75impl Default for CachedArgs {
76 fn default() -> Self {
77 CachedArgs {
78 duration: Duration::from_secs(15 * 60),
79 key: CachedKeyArgs {
80 git_head: false,
81 git_base: false,
82 git_head_diff: false,
83 git_repo_dir: false,
84 git_pending: false,
85 env: vec![],
86 text: vec![],
87 no_dir: false,
88 no_command: false,
89 no_direct_env: false,
90 },
91 no_cached_output: false,
92 exit_code: false,
93 verbose: false,
94 cmd: CommandArgs::Cmd(Vec::new()),
95 }
96 }
97}
98
99#[test]
100fn test_cli_args() {
101 let mut args = CachedArgs::try_parse_from(&["cmd", "ls"]).unwrap();
102 assert!(!args.any_explicit_key());
103 assert_eq!(args, CachedArgs {
104 cmd: CommandArgs::Cmd(vec!["ls".to_owned()]),
105 ..CachedArgs::default()
106 });
107 args = CachedArgs::try_parse_from(&["cmd", "--duration", "1 year", "ls"]).unwrap();
108 assert!(!args.any_explicit_key());
109 args = CachedArgs::try_parse_from(&["cmd", "-d1y", "--git-head", "--git-pending", "ls", "-a", "-l", "-s", "-h"]).unwrap();
110 assert!(args.any_explicit_key());
111 args = CachedArgs::try_parse_from(&["cmd", "-d1y", "--text", "string", "ls", "-alsh"]).unwrap();
112 assert!(args.any_explicit_key());
113 args = CachedArgs::try_parse_from(&["cmd", "-d1y", "-gpe", "ENV_VAR", "-CDEt", "string", "-t", "another string", "--", "ls"]).unwrap();
114 assert!(args.any_explicit_key());
115}