porta-rs 0.1.1

Zero-trust CGNAT bypass via WireGuard tunnel
use anyhow::{Context, Result};
use std::fs;
use std::path::PathBuf;

/// UFW rules persistence manager
pub struct UfwPersistence {
    rules_path: PathBuf,
}

impl UfwPersistence {
    pub fn new() -> Self {
        Self {
            rules_path: PathBuf::from("/etc/ufw/user.rules"),
        }
    }

    /// Save current Porta rules to a persistent file
    pub fn save_rules(&self, rules: &[String]) -> Result<()> {
        let content = rules.join("\n");
        fs::write(&self.rules_path, content)
            .context(format!("Failed to write UFW rules to {}", self.rules_path.display()))?;

        tracing::info!("UFW rules saved to {}", self.rules_path.display());
        Ok(())
    }

    /// Load rules from persistent file
    pub fn load_rules(&self) -> Result<Vec<String>> {
        if !self.rules_path.exists() {
            return Ok(Vec::new());
        }

        let content = fs::read_to_string(&self.rules_path)
            .context(format!("Failed to read UFW rules from {}", self.rules_path.display()))?;

        Ok(content.lines().map(|s| s.to_string()).collect())
    }

    /// Backup current UFW rules
    pub fn backup(&self) -> Result<PathBuf> {
        let backup_path = PathBuf::from(format!(
            "/etc/ufw/user.rules.bak.{}",
            chrono::Utc::now().format("%Y%m%d%H%M%S")
        ));

        if self.rules_path.exists() {
            fs::copy(&self.rules_path, &backup_path)
                .context("Failed to backup UFW rules")?;

            tracing::info!("UFW rules backed up to {}", backup_path.display());
        }

        Ok(backup_path)
    }
}

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