1use std::marker::PhantomData;
2
3use anyhow::anyhow;
4use prettytable::Table;
5
6use crate::bucket::BucketPtr;
7use crate::GlobalInstance;
8use crate::Result;
9
10mod copy;
11mod list;
12mod remove;
13mod sign;
14
15pub trait Command {
16 const COMMAND: &'static str;
17 const NUM_ARGS: usize;
18 const HELP: &'static str;
19
20 async fn execute(buckets: &[BucketPtr], path: &str) -> Result<Table>;
21}
22
23pub struct CommandHelper<C> {
24 _c: PhantomData<C>,
25}
26
27impl<C: Command> CommandHelper<C> {
28 pub const COMMAND: &'static str = C::COMMAND;
29
30 pub async fn execute(args: &[String]) -> Result<Table> {
35 if args.len() != C::NUM_ARGS {
36 return Err(anyhow!(
37 "Command {} requires {} argument(s), but got {}. Usage: {}",
38 C::COMMAND,
39 C::NUM_ARGS,
40 args.len(),
41 C::HELP
42 ));
43 }
44
45 let instance = GlobalInstance::instance();
46
47 let mut buckets = Vec::with_capacity(C::NUM_ARGS - 1);
48 for b in args[0..C::NUM_ARGS - 1].iter() {
49 if b == "*" {
50 buckets.extend(instance.get_buckets());
51 } else {
52 let op = instance
53 .get_bucket(b)
54 .ok_or(anyhow!("Bucket '{}' not found", b))?;
55 buckets.push(op);
56 }
57 }
58 let path = &args.last().unwrap();
59
60 C::execute(&buckets, path).await
61 }
62}
63
64pub type CommandCopy = CommandHelper<copy::Copy>;
65pub type CommandList = CommandHelper<list::List>;
66pub type CommandRemove = CommandHelper<remove::Remove>;
67pub type CommandSign = CommandHelper<sign::Sign>;
68
69pub const COMMANDS: [&str; 4] = [
70 CommandCopy::COMMAND,
71 CommandList::COMMAND,
72 CommandRemove::COMMAND,
73 CommandSign::COMMAND,
74];