1pub mod config;
3
4pub mod wallet;
6
7pub mod commands;
9
10use std::{ops::Deref, path::PathBuf};
11
12use clap::Parser;
13use commands::{Command, CommandClient, Commands, Context};
14use config::Config;
15use eyre::OptionExt;
16use figment::{
17 providers::{Env, Format, Serialized, Toml},
18 Figment,
19};
20
21const ENV_PREFIX: &str = "GMSOL_";
22const CONFIG_DIR: &str = "gmsol";
23
24const DOT_ALIAS: &str = "__";
28
29#[derive(Debug)]
31pub struct Cli(Inner);
32
33impl Cli {
34 pub fn init() -> eyre::Result<Self> {
36 let cli = Inner::parse();
37
38 let config_path = cli.find_config()?;
39 let Inner {
40 config,
41 command,
42 verbose,
43 ..
44 } = cli;
45
46 let config = Figment::new()
47 .merge(Serialized::defaults(config))
48 .join(Env::prefixed(ENV_PREFIX).split(DOT_ALIAS))
49 .join(Toml::file(config_path.clone()))
50 .extract()?;
51
52 Ok(Self(Inner {
53 config_path: Some(config_path),
54 config,
55 command,
56 verbose,
57 }))
58 }
59}
60
61impl Deref for Cli {
62 type Target = Inner;
63
64 fn deref(&self) -> &Self::Target {
65 &self.0
66 }
67}
68
69#[derive(Debug, Parser)]
71#[command(
72 version = concat!(
73 env!("CARGO_PKG_VERSION"), " (",
74 env!("VERGEN_BUILD_DATE"), ")"
75 ),
76 long_version = concat!(
77 env!("CARGO_PKG_VERSION"), "\n",
78 "Built: ", env!("VERGEN_BUILD_TIMESTAMP"), "\n",
79 "Git commit: ", env!("VERGEN_GIT_SHA"), "\n",
80 "Rustc version: ", env!("VERGEN_RUSTC_SEMVER"), "\n",
81 "Enabled features: ", env!("VERGEN_CARGO_FEATURES"), "\n",
82 "Debug: ", env!("VERGEN_CARGO_DEBUG"),
83 ),
84 about = None,
85 long_about = None,
86)]
87pub struct Inner {
88 #[clap(long = "config", short)]
90 config_path: Option<PathBuf>,
91 #[clap(long, short, global = true)]
93 verbose: bool,
94 #[command(flatten)]
96 config: Config,
97 #[command(subcommand)]
99 command: Commands,
100}
101
102impl Inner {
103 fn find_config(&self) -> eyre::Result<PathBuf> {
104 use etcetera::{choose_base_strategy, BaseStrategy};
105
106 match self.config_path.as_ref() {
107 Some(path) => Ok(path.clone()),
108 None => {
109 let strategy = choose_base_strategy()?;
110 Ok(strategy.config_dir().join(CONFIG_DIR).join("config.toml"))
111 }
112 }
113 }
114
115 pub async fn execute(&self) -> eyre::Result<()> {
117 let config_path = self
118 .config_path
119 .as_ref()
120 .ok_or_eyre("config path is not set")?;
121 #[cfg(feature = "remote-wallet")]
122 let mut wallet_manager = None;
123 let client = if self.command.is_client_required() {
124 cfg_if::cfg_if! {
125 if #[cfg(feature = "remote-wallet")] {
126 Some(CommandClient::new(&self.config, &mut wallet_manager, self.verbose)?)
127 } else {
128 Some(CommandClient::new(&self.config, self.verbose)?)
129 }
130 }
131 } else {
132 None
133 };
134 let store = self.config.store_address();
135 self.command
136 .execute(Context::new(
137 store,
138 config_path,
139 &self.config,
140 client.as_ref(),
141 self.verbose,
142 ))
143 .await
144 }
145}