infrarust 1.1.2

A Rust adaptation of the Infrared Minecraft proxy
Documentation
use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock;
use tracing::{debug, debug_span, instrument, Instrument};
use wildmatch::WildMatch;

use crate::{core::config::ServerConfig, telemetry::TELEMETRY};

#[derive(Clone)]
pub struct ConfigurationService {
    configurations: Arc<RwLock<HashMap<String, Arc<ServerConfig>>>>,
}

impl Default for ConfigurationService {
    fn default() -> Self {
        Self::new()
    }
}

impl ConfigurationService {
    #[instrument(name = "create_config_service")]
    pub fn new() -> Self {
        debug!("Creating new configuration service");
        Self {
            configurations: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    #[instrument(skip(self), fields(domain = %domain))]
    pub async fn find_server_by_domain(&self, domain: &str) -> Option<Arc<ServerConfig>> {
        debug!("Finding server by domain");
        let domain = domain.to_lowercase();
        let configs = self.configurations.read().await;
        let result = configs
            .values()
            .find(|server| {
                server
                    .domains
                    .iter()
                    .any(|pattern| WildMatch::new(pattern).matches(&domain))
            })
            .cloned();

        debug!(found = result.is_some(), "Domain lookup result");
        result
    }

    #[instrument(skip(self), fields(ip = %ip))]
    pub async fn find_server_by_ip(&self, ip: &str) -> Option<Arc<ServerConfig>> {
        debug!("Finding server by IP");
        let configs = self.configurations.read().await;
        let result = configs
            .iter()
            .find(|(_, server)| server.addresses.contains(&ip.to_string()))
            .map(|(_, server)| Arc::clone(server));

        debug!(found = result.is_some(), "IP lookup result");
        result
    }

    pub async fn update_configurations(&self, configs: Vec<ServerConfig>) {
        let span = debug_span!(
            "config_service: update_config_store",
            config_count = configs.len()
        );

        async {
            let mut config_lock = self.configurations.write().await;
            for config in configs {
                debug!(
                    config_id = %config.config_id,
                    domains = ?config.domains,
                    "Updating configuration"
                );
                TELEMETRY.update_backend_count(1, &config.config_id);
                config_lock.insert(config.config_id.clone(), Arc::new(config));
            }
        }
        .instrument(span)
        .await;
    }

    #[instrument(skip(self), fields(config_id = %config_id))]
    pub async fn remove_configuration(&self, config_id: &str) {
        let mut config_lock = self.configurations.write().await;
        debug!(
            config_id = %config_id,
            "Removing configuration"
        );

        TELEMETRY.update_backend_count(-1, config_id);
        if config_lock.remove(config_id).is_some() {
            debug!("Configuration removed successfully");
        } else {
            debug!("Configuration not found for removal");
        }
    }
}