feat: Tweaked the implementation for environment variables in the config a bit

This commit is contained in:
2025-01-26 14:59:09 -07:00
parent 6f3c6ec840
commit c4ace8c53f
2 changed files with 128 additions and 72 deletions
+64 -70
View File
@@ -3,6 +3,7 @@ use std::process;
use anyhow::{anyhow, Error};
use colored::Colorize;
use log::{debug, error};
use regex::Regex;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::Sender;
use tokio_util::sync::CancellationToken;
@@ -213,71 +214,6 @@ pub struct Data<'a> {
pub sonarr_data: SonarrData<'a>,
}
fn deserialize_env_var<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: String = String::deserialize(deserializer)?;
let interpolated = interpolate_env_vars(&s);
Ok(interpolated)
}
fn deserialize_optional_env_var<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
match s {
Some(value) => {
let interpolated = interpolate_env_vars(&value);
Ok(Some(interpolated))
}
None => Ok(None),
}
}
fn deserialize_u16_env_var<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
match s {
Some(value) => {
let interpolated = interpolate_env_vars(&value);
interpolated
.parse::<u16>()
.map(Some)
.map_err(serde::de::Error::custom)
}
None => Ok(None),
}
}
fn interpolate_env_vars(s: &str) -> String {
let mut result = s.to_string();
let start = "${";
let end = "}";
while let Some(start_index) = result.find(start) {
let end_index = result[start_index..]
.find(end)
.map(|i| start_index + i + end.len());
if let Some(end_index) = end_index {
let var_name = &result[start_index + start.len()..end_index - end.len()];
if let Ok(value) = std::env::var(var_name) {
// Match found; interpolate
result.replace_range(start_index..end_index, &value);
} else {
break; // No var match found; interpret it literally
}
} else {
break; // No closing brace found
}
}
result
}
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
pub struct AppConfig {
pub radarr: Option<ServarrConfig>,
@@ -325,15 +261,15 @@ impl AppConfig {
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ServarrConfig {
#[serde(deserialize_with = "deserialize_optional_env_var")]
#[serde(default, deserialize_with = "deserialize_optional_env_var")]
pub host: Option<String>,
#[serde(deserialize_with = "deserialize_u16_env_var")]
#[serde(default, deserialize_with = "deserialize_u16_env_var")]
pub port: Option<u16>,
#[serde(deserialize_with = "deserialize_optional_env_var")]
#[serde(default, deserialize_with = "deserialize_optional_env_var")]
pub uri: Option<String>,
#[serde(deserialize_with = "deserialize_env_var")]
#[serde(default, deserialize_with = "deserialize_env_var")]
pub api_token: String,
#[serde(deserialize_with = "deserialize_optional_env_var")]
#[serde(default, deserialize_with = "deserialize_optional_env_var")]
pub ssl_cert_path: Option<String>,
}
@@ -362,3 +298,61 @@ pub fn log_and_print_error(error: String) {
error!("{}", error);
eprintln!("error: {}", error.red());
}
fn deserialize_env_var<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: String = String::deserialize(deserializer)?;
let interpolated = interpolate_env_vars(&s);
Ok(interpolated)
}
fn deserialize_optional_env_var<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
match s {
Some(value) => {
let interpolated = interpolate_env_vars(&value);
Ok(Some(interpolated))
}
None => Ok(None),
}
}
fn deserialize_u16_env_var<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;
match s {
Some(value) => {
let interpolated = interpolate_env_vars(&value);
interpolated
.parse::<u16>()
.map(Some)
.map_err(serde::de::Error::custom)
}
None => Ok(None),
}
}
fn interpolate_env_vars(s: &str) -> String {
let result = s.to_string();
let scrubbing_regex = Regex::new(r#"[\s\{\}!\$^\(\)\[\]\\\|`'"]+"#).unwrap();
let var_regex = Regex::new(r"\$\{(.*?)\}").unwrap();
var_regex
.replace_all(s, |caps: &regex::Captures<'_>| {
if let Some(mat) = caps.get(1) {
if let Ok(value) = std::env::var(mat.as_str()) {
return scrubbing_regex.replace_all(&value, "").to_string();
}
}
scrubbing_regex.replace_all(&result, "").to_string()
})
.to_string()
}