summaryrefslogtreecommitdiffstatshomepage
path: root/src/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/config.rs')
-rw-r--r--src/config.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..2f3e5ed
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,143 @@
+use std::{
+ env, fs,
+ path::{Path, PathBuf},
+};
+
+use anyhow::{bail, Context, Result};
+use clap::Parser;
+use const_format::{formatcp, map_ascii_case, Case};
+use serde::Deserialize;
+
+use crate::{
+ actions::AnyActionConfig, blockchain::ElectrumConfig, message::MessageConfig,
+ wallets::WalletConfig,
+};
+
+#[derive(Parser, Debug)]
+#[command(version, about)]
+pub struct Args {
+ /// Path to toml configuration file
+ config: Option<String>,
+ /// Perform configured actions on a test notification
+ #[arg(short, long)]
+ test: bool,
+ /// Notify for every past transaction (careful: if you have a long transaction history, this
+ /// can SPAM your configured actions
+ #[arg(short, long)]
+ notify_past_txs: bool,
+}
+
+impl Args {
+ pub fn config(&self) -> Option<&str> {
+ self.config.as_deref()
+ }
+
+ pub fn test(&self) -> bool {
+ self.test
+ }
+
+ pub fn notify_past_txs(&self) -> bool {
+ self.notify_past_txs
+ }
+}
+
+fn get_config_filename() -> &'static str {
+ formatcp!("{}.toml", env!("CARGO_PKG_NAME"))
+}
+
+fn get_config_env_var() -> &'static str {
+ formatcp!(
+ "{}_CONFIG",
+ map_ascii_case!(Case::Upper, env!("CARGO_PKG_NAME"))
+ )
+}
+
+fn get_cwd_config_path() -> PathBuf {
+ PathBuf::from(".").join(get_config_filename())
+}
+
+fn get_config_path_impl(user_config_dir: &Path) -> PathBuf {
+ user_config_dir
+ .join(env!("CARGO_PKG_NAME"))
+ .join(get_config_filename())
+}
+
+fn get_user_config_path() -> Option<PathBuf> {
+ dirs::config_dir().map(|p| get_config_path_impl(&p))
+}
+
+fn get_system_config_path() -> PathBuf {
+ get_config_path_impl(&systemd_directories::config_dir().unwrap_or(PathBuf::from("/etc")))
+}
+
+fn get_config_path(maybe_arg_config: &Option<&str>) -> Result<PathBuf> {
+ if let Some(arg_path) = maybe_arg_config {
+ return Ok(PathBuf::from(arg_path));
+ }
+
+ if let Ok(env_path) = env::var(get_config_env_var()) {
+ return Ok(PathBuf::from(env_path));
+ }
+
+ let cwd_config_path = get_cwd_config_path();
+ if cwd_config_path.try_exists().is_ok_and(|x| x) {
+ return Ok(cwd_config_path);
+ }
+
+ if let Some(user_config_path) = get_user_config_path() {
+ if user_config_path.try_exists().is_ok_and(|x| x) {
+ return Ok(user_config_path);
+ }
+ }
+
+ let system_config_path = get_system_config_path();
+ if system_config_path.try_exists().is_ok_and(|x| x) {
+ return Ok(system_config_path);
+ }
+
+ bail!(
+ "no configuration file was passed as first argument, nor by the '{}' environment variable, nor did one exist in the default search paths: '{}', '{}', '{}'",
+ get_config_env_var(),
+ get_cwd_config_path().display(),
+ get_user_config_path().unwrap_or_default().display(),
+ get_system_config_path().display()
+ );
+}
+
+#[derive(Deserialize, Debug)]
+pub struct Config {
+ wallets: Vec<WalletConfig>,
+ #[serde(default)]
+ electrum: ElectrumConfig,
+ #[serde(default)]
+ message: MessageConfig,
+ #[serde(default)]
+ actions: Vec<AnyActionConfig>,
+}
+
+impl Config {
+ pub fn electrum(&self) -> &ElectrumConfig {
+ &self.electrum
+ }
+
+ pub fn wallets(&self) -> &[WalletConfig] {
+ &self.wallets
+ }
+
+ pub fn message(&self) -> &MessageConfig {
+ &self.message
+ }
+
+ pub fn actions(&self) -> &[AnyActionConfig] {
+ &self.actions
+ }
+}
+
+pub fn get_config(maybe_arg_config: &Option<&str>) -> Result<Config> {
+ let config_path = get_config_path(maybe_arg_config)?;
+ info!("reading configuration from '{}'", config_path.display());
+ let config_content = fs::read_to_string(&config_path)
+ .with_context(|| format!("could not read config file '{}'", config_path.display()))?;
+ toml::from_str(&config_content)
+ .with_context(|| format!("could not parse config file '{}'", config_path.display(),))
+}