freeswitch-log-parser 0.17.1

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 anyhow::Context;
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)
    }
}

fn env_dir(name: &str) -> anyhow::Result<Option<PathBuf>> {
    Ok(crate::env::var(name)?.map(PathBuf::from))
}

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

pub fn load_config(explicit: Option<&Path>) -> anyhow::Result<Config> {
    let Some(path) = find_config(explicit)? else {
        return Ok(Config::default());
    };
    let content =
        std::fs::read_to_string(&path).with_context(|| format!("reading {}", path.display()))?;
    serde_yml::from_str(&content).with_context(|| format!("parsing {}", path.display()))
}