Commit

Author:

Hash:

Timestamp:

+72 -38 +/-3 browse

Kevin Schoon [me@kevinschoon.com]

8634ef5b3eafe0a9c2cdf27ed03e960e4a5ba7e2

Sun, 31 Dec 2023 17:22:03 +0000 (1.5 years ago)

make ayllu-mail cli derive based; add complete subcommand
1diff --git a/Cargo.lock b/Cargo.lock
2index 41e95f4..68b08a3 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -591,6 +591,7 @@ dependencies = [
6 "capnp",
7 "capnp-rpc",
8 "clap 4.4.8",
9+ "clap_complete",
10 "futures",
11 "mailpot",
12 "melib 0.8.4",
13 @@ -978,6 +979,15 @@ dependencies = [
14 ]
15
16 [[package]]
17+ name = "clap_complete"
18+ version = "4.4.5"
19+ source = "registry+https://github.com/rust-lang/crates.io-index"
20+ checksum = "a51919c5608a32e34ea1d6be321ad070065e17613e168c5b6977024290f2630b"
21+ dependencies = [
22+ "clap 4.4.8",
23+ ]
24+
25+ [[package]]
26 name = "clap_derive"
27 version = "4.4.7"
28 source = "registry+https://github.com/rust-lang/crates.io-index"
29 diff --git a/ayllu-mail/Cargo.toml b/ayllu-mail/Cargo.toml
30index 4f727d2..5fb51f0 100644
31--- a/ayllu-mail/Cargo.toml
32+++ b/ayllu-mail/Cargo.toml
33 @@ -8,7 +8,7 @@ ayllu_api = {workspace = true}
34 ayllu_config = {workspace = true}
35 ayllu_rpc = {workspace = true}
36 serde = { version = "1.0.188", features = ["derive"] }
37- clap = "4.4.6"
38+ clap = { version = "4.4.6", features = ["derive"] }
39 tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
40 tracing = "0.1.37"
41 tokio = { version = "1.33.0", features = ["full"] }
42 @@ -17,3 +17,4 @@ futures = "0.3.28"
43 capnp = "0.18.1"
44 melib = "0.8.2"
45 mailpot = { git = "https://ayllu-forge.org/forks/mailpot", branch = "ayllu-dev"}
46+ clap_complete = "4.4.5"
47 diff --git a/ayllu-mail/src/main.rs b/ayllu-mail/src/main.rs
48index d0a90bc..e09101b 100644
49--- a/ayllu-mail/src/main.rs
50+++ b/ayllu-mail/src/main.rs
51 @@ -1,54 +1,77 @@
52- use std::path::Path;
53+ use std::io::stdout;
54+ use std::path::PathBuf;
55+ use std::str::FromStr;
56
57- use clap::{arg, value_parser, Command};
58+ use clap::{arg, Command, CommandFactory, Parser, Subcommand};
59+ use clap_complete::{generate, Generator, Shell};
60 use tokio::{runtime::Builder as TokioBuilder, task::LocalSet};
61+ use tracing::Level;
62
63 mod config;
64 mod declarative;
65 mod server;
66
67+ #[derive(Parser)]
68+ #[command(author, version, about, long_about = None)]
69+ #[command(name = "ayllu-mail")]
70+ #[command(about = "Mailing List Management plugin for Ayllu")]
71+ struct Cli {
72+ /// Path to your configuration file
73+ #[arg(short, long, value_name = "FILE")]
74+ config: Option<PathBuf>,
75+
76+ /// Sets the logging level
77+ #[arg(short, long, value_name = "LEVEL")]
78+ level: Option<Level>,
79+
80+ #[command(subcommand)]
81+ command: Commands,
82+ }
83+
84+ #[derive(Subcommand, Debug, PartialEq)]
85+ enum Commands {
86+ /// generate autocomplete commands for common shells
87+ Complete {
88+ #[arg(long)]
89+ shell: Shell,
90+ },
91+ /// run the rpc server and mailing list manager
92+ Serve {},
93+ }
94+
95+ fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
96+ generate(gen, cmd, cmd.get_name().to_string(), &mut stdout());
97+ }
98+
99 fn main() -> Result<(), Box<dyn std::error::Error>> {
100- let command = Command::new("ayllu-mail")
101- .about("ayllu email service")
102- .arg(
103- arg!(-c --config <FILE> "optional path to a configuration file")
104- .id("config")
105- .required(false)
106- .value_parser(value_parser!(String)),
107- )
108- .arg(
109- arg!(-l --level <LEVEL> "logging level [ERROR,WARN,INFO,DEBUG,TRACE]")
110- .id("level")
111- .required(false)
112- .value_parser(value_parser!(String)),
113- );
114- let matches = command.get_matches();
115- let config_path = matches.get_one::<String>("config").map(Path::new);
116- let ayllu_config = config::load(config_path)?;
117- let log_level = matches
118- .get_one::<String>("level")
119- .cloned()
120- .unwrap_or(ayllu_config.log_level.clone());
121+ let cli = Cli::parse();
122+ let ayllu_config = config::load(cli.config.as_ref().map(|cfg| cfg.as_path()))?;
123+ let config_level = Level::from_str(&ayllu_config.log_level)?;
124 tracing_subscriber::fmt()
125 .compact()
126 .with_line_number(true)
127 .with_level(true)
128- // tokei will spam the console unless this is set
129- .with_env_filter(format!(
130- "{},tokei::language::language_type=error",
131- log_level
132- ))
133+ .with_max_level(cli.level.unwrap_or(config_level))
134 .init();
135 tracing::info!("logger initialized");
136 declarative::initialize(&ayllu_config)?;
137- TokioBuilder::new_current_thread()
138- .enable_all()
139- .build()
140- .unwrap()
141- .block_on(async move {
142- LocalSet::new()
143- .run_until(server::serve(&ayllu_config))
144- .await
145- })?;
146+ match cli.command {
147+ Commands::Complete { shell } => {
148+ let mut cmd = Cli::command();
149+ print_completions(shell, &mut cmd);
150+ }
151+ Commands::Serve {} => {
152+ TokioBuilder::new_current_thread()
153+ .enable_all()
154+ .build()
155+ .unwrap()
156+ .block_on(async move {
157+ LocalSet::new()
158+ .run_until(server::serve(&ayllu_config))
159+ .await
160+ })?;
161+ }
162+ }
163+
164 Ok(())
165 }