i18n-audit 0.1.0

Rust i18n audit library and CLI for scanning translation usage, missing keys, and unused keys
Documentation
use std::collections::BTreeMap;
use std::path::Path;

use crate::support::PathNormalizer;

#[derive(Debug, Clone)]
pub struct EnvConfig {
    values: BTreeMap<String, String>,
}

impl EnvConfig {
    pub fn load(project_root: &str, env_file: &str) -> Self {
        let env_path = if PathNormalizer::is_absolute(env_file) {
            env_file.to_string()
        } else {
            PathNormalizer::normalize(&Path::new(project_root).join(env_file).to_string_lossy())
        };

        if Path::new(&env_path).is_file() {
            let _ = dotenvy::from_path_override(&env_path);
        }

        let mut values = BTreeMap::new();
        for (key, value) in std::env::vars() {
            values.insert(key, value);
        }

        Self { values }
    }

    pub fn get_string(&self, key: &str, default: &str) -> String {
        self.values
            .get(key)
            .map(|value| value.trim().to_string())
            .filter(|value| !value.is_empty())
            .unwrap_or_else(|| default.to_string())
    }

    pub fn get_bool(&self, key: &str, default: bool) -> bool {
        match self.get_string(key, if default { "true" } else { "false" }).to_ascii_lowercase().as_str() {
            "1" | "true" | "yes" | "on" => true,
            _ => false,
        }
    }

    pub fn get_csv_array(&self, key: &str, default: Vec<String>) -> Vec<String> {
        let raw = self.get_string(key, "");
        if raw.is_empty() {
            return default;
        }

        raw.split(',')
            .map(|item| item.trim().to_string())
            .filter(|item| !item.is_empty())
            .collect()
    }
}