ufw-rule-parser 0.1.0

parser for a ufw-like firewall rule, including internal/external address keywords.
Documentation
use std::{fs, path::PathBuf};

use anyhow::Result;
use clap::{Parser, Subcommand};
use serde_json;
use ufw_rule_parser::parse_rules;

#[derive(Parser)]
#[command(
    name = "ufw rule parser",
    version,
    about = "cli for parsing ufw-style firewall rules into a typed ast."
)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    Parse {
        #[arg(value_name = "FILE")]
        path: PathBuf,
        #[arg(short, long)]
        json: bool,
        #[arg(short, long, value_name = "OUTPUT")]
        output: Option<PathBuf>,
    },
    Credits,
}

fn main() -> Result<()> {
    let cli = Cli::parse();

    match cli.command {
        Commands::Parse { path, json, output } => parse_file(path, json, output)?,
        Commands::Credits => {
            println!("ufw rule parser built with pest, anyhow, and thiserror.");
        }
    }

    Ok(())
}

fn parse_file(path: PathBuf, json: bool, output: Option<PathBuf>) -> Result<()> {
    let contents = fs::read_to_string(&path)?;
    let rules = parse_rules(&contents)?;

    if let Some(output_path) = output {
        if !json {
            return Err(anyhow::anyhow!("--output flag requires --json flag"));
        }
        let json_str = serde_json::to_string_pretty(&rules)?;
        fs::write(&output_path, json_str)?;
        eprintln!("JSON written to: {}", output_path.display());
    } else if json {
        println!("{}", serde_json::to_string_pretty(&rules)?);
    } else {
        println!("{rules:#?}");
    }
    Ok(())
}