freeswitch-log-parser 0.4.3

Parser for FreeSWITCH log files — handles compressed .xz files, multi-line dumps, truncated buffers, and stateful UUID/timestamp tracking
Documentation
use std::path::{Path, PathBuf};

use serde::Deserialize;

#[derive(Deserialize, Default)]
pub struct Config {
    #[serde(default)]
    pub monitor: MonitorConfig,
    #[serde(default)]
    pub tools: Vec<Tool>,
}

#[derive(Deserialize)]
pub struct MonitorConfig {
    #[serde(default = "default_linger")]
    pub hangup_linger_seconds: u64,
}

impl Default for MonitorConfig {
    fn default() -> Self {
        MonitorConfig {
            hangup_linger_seconds: default_linger(),
        }
    }
}

fn default_linger() -> u64 {
    3600
}

#[derive(Deserialize, Clone)]
pub struct Tool {
    pub name: String,
    pub command: String,
}

impl Tool {
    pub fn expand_command(&self, uuid: &str) -> String {
        self.command.replace("{{uuid}}", uuid)
    }
}

pub fn find_config(explicit: Option<&Path>) -> Option<PathBuf> {
    if let Some(p) = explicit {
        return Some(p.to_path_buf());
    }
    if let Ok(p) = std::env::var("FSLOG_CONFIG") {
        return Some(PathBuf::from(p));
    }
    if let Ok(xdg) = std::env::var("XDG_CONFIG_HOME") {
        let p = PathBuf::from(xdg).join("fslog/config.yaml");
        if p.exists() {
            return Some(p);
        }
    }
    if let Ok(home) = std::env::var("HOME") {
        let p = PathBuf::from(home).join(".config/fslog/config.yaml");
        if p.exists() {
            return Some(p);
        }
    }
    let p = PathBuf::from("/etc/fslog/config.yaml");
    if p.exists() {
        return Some(p);
    }
    None
}

pub fn load_config(explicit: Option<&Path>) -> Result<Config, String> {
    let path = match find_config(explicit) {
        Some(p) => p,
        None => return Ok(Config::default()),
    };
    let content = std::fs::read_to_string(&path).map_err(|e| format!("{}: {e}", path.display()))?;
    serde_yml::from_str(&content).map_err(|e| format!("{}: {e}", path.display()))
}