mithril_aggregator/commands/
mod.rs1mod config_association;
2mod database_command;
3mod era_command;
4mod genesis_command;
5mod serve_command;
6mod tools_command;
7
8use anyhow::anyhow;
9use clap::{CommandFactory, Parser, Subcommand};
10use config::{builder::DefaultState, ConfigBuilder, Map, Source, Value};
11use mithril_cli_helper::{register_config_value, register_config_value_option};
12use mithril_common::StdResult;
13use mithril_doc::{Documenter, GenerateDocCommands, StructDoc};
14use slog::{debug, Level, Logger};
15use std::{collections::HashMap, path::PathBuf};
16
17use crate::{extract_all, DefaultConfiguration};
18
19#[derive(Debug, Clone, Subcommand)]
21pub enum MainCommand {
22 Genesis(genesis_command::GenesisCommand),
23 Era(era_command::EraCommand),
24 Serve(serve_command::ServeCommand),
25 Tools(tools_command::ToolsCommand),
26 Database(database_command::DatabaseCommand),
27 #[clap(alias("doc"), hide(true))]
28 GenerateDoc(GenerateDocCommands),
29}
30pub enum CommandType {
32 Server,
34
35 CommandLine,
37}
38
39impl MainCommand {
40 pub async fn execute(
41 &self,
42 root_logger: Logger,
43 config_builder: ConfigBuilder<DefaultState>,
44 ) -> StdResult<()> {
45 match self {
46 Self::Genesis(cmd) => cmd.execute(root_logger, config_builder).await,
47 Self::Era(cmd) => cmd.execute(root_logger).await,
48 Self::Serve(cmd) => cmd.execute(root_logger, config_builder).await,
49 Self::Tools(cmd) => cmd.execute(root_logger, config_builder).await,
50 Self::Database(cmd) => cmd.execute(root_logger, config_builder).await,
51 Self::GenerateDoc(cmd) => {
52 let commands_configs =
53 Self::extract_config(Self::format_crate_name_to_config_key());
54
55 cmd.execute_with_configurations(&mut MainOpts::command(), commands_configs)
56 .map_err(|message| anyhow!(message))
57 }
58 }
59 }
60
61 pub fn extract_config(command_path: String) -> HashMap<String, StructDoc> {
62 extract_all!(
63 command_path,
64 MainCommand,
65 Database = { database_command::DatabaseCommand },
66 Era = { era_command::EraCommand },
67 Genesis = { genesis_command::GenesisCommand },
68 Serve = { serve_command::ServeCommand },
69 Tools = { tools_command::ToolsCommand },
70 GenerateDoc = {},
71 )
72 }
73
74 fn format_crate_name_to_config_key() -> String {
75 env!("CARGO_PKG_NAME").replace("-", "")
76 }
77
78 pub fn command_type(&self) -> CommandType {
79 match self {
80 MainCommand::Serve(_) => CommandType::Server,
81 MainCommand::Genesis(_) => CommandType::CommandLine,
82 MainCommand::Era(_) => CommandType::CommandLine,
83 MainCommand::Tools(_) => CommandType::CommandLine,
84 MainCommand::Database(_) => CommandType::CommandLine,
85 MainCommand::GenerateDoc(_) => CommandType::CommandLine,
86 }
87 }
88}
89
90#[derive(Documenter, Parser, Debug, Clone)]
92#[command(version)]
93pub struct MainOpts {
94 #[clap(subcommand)]
96 pub command: MainCommand,
97
98 #[clap(short, long, default_value = "dev")]
100 pub run_mode: String,
101
102 #[clap(short, long, action = clap::ArgAction::Count)]
104 #[example = "Parsed from the number of occurrences: `-v` for `Warning`, `-vv` for `Info`, `-vvv` for `Debug` and `-vvvv` for `Trace`"]
105 pub verbose: u8,
106
107 #[clap(long)]
109 pub db_directory: Option<PathBuf>,
110
111 #[clap(long, default_value = "./config")]
113 pub config_directory: PathBuf,
114}
115
116impl Source for MainOpts {
117 fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
118 Box::new(self.clone())
119 }
120
121 fn collect(&self) -> Result<Map<String, Value>, config::ConfigError> {
122 let mut result = Map::new();
123 let namespace = "clap arguments".to_string();
124
125 register_config_value_option!(result, &namespace, self.db_directory, |v: PathBuf| format!(
126 "{}",
127 v.to_string_lossy()
128 ));
129
130 Ok(result)
131 }
132}
133
134impl MainOpts {
135 pub async fn execute(&self, root_logger: Logger) -> StdResult<()> {
137 let config_file_path = self
138 .config_directory
139 .join(format!("{}.json", self.run_mode));
140 let config_builder = config::Config::builder()
141 .add_source(DefaultConfiguration::default())
142 .add_source(
143 config::File::with_name(&config_file_path.to_string_lossy()).required(false),
144 )
145 .add_source(config::Environment::default().separator("__"))
146 .add_source(self.clone());
147 debug!(root_logger, "Started"; "run_mode" => &self.run_mode, "node_version" => env!("CARGO_PKG_VERSION"));
148
149 self.command.execute(root_logger, config_builder).await
150 }
151
152 pub fn log_level(&self) -> Level {
154 match self.verbose {
155 0 => Level::Error,
156 1 => Level::Warning,
157 2 => Level::Info,
158 3 => Level::Debug,
159 _ => Level::Trace,
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_format_crate_name_as_config_key() {
170 let crate_name = MainCommand::format_crate_name_to_config_key();
171
172 assert_eq!(crate_name, "mithrilaggregator");
173 }
174}