backdisco 0.4.0

Discover backend origins from CDN frontends using LLM-assisted pattern analysis and brute force enumeration
use anyhow::Result;
use colored::*;
use indicatif::{ProgressBar, ProgressStyle};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

pub struct Output {
    verbose: u8,
    file: Option<File>,
}

impl Output {
    pub fn new(verbose: u8, output_path: Option<PathBuf>) -> Result<Self> {
        let file = if let Some(path) = &output_path {
            Some(File::create(path)?)
        } else {
            None
        };

        Ok(Output { verbose, file })
    }

    fn write(&mut self, plain_message: &str, colored_message: Option<&str>) {
        // Write plain text to file if specified
        if let Some(ref mut file) = self.file {
            let _ = writeln!(file, "{}", plain_message);
        }
        // Write colored text to terminal
        if let Some(colored) = colored_message {
            println!("{}", colored);
        } else {
            println!("{}", plain_message);
        }
    }

    pub fn info(&mut self, message: &str) {
        if self.verbose >= 1 {
            let styled = format!("{} {}", "".cyan(), message);
            self.write(message, Some(&styled));
        }
    }

    pub fn success(&mut self, message: &str) {
        if self.verbose >= 1 {
            let styled = format!("{} {}", "".green(), message);
            self.write(message, Some(&styled));
        }
    }

    pub fn error(&mut self, message: &str) {
        let styled = format!("{} {}", "".red(), message);
        self.write(message, Some(&styled));
    }

    pub fn debug(&mut self, message: &str) {
        if self.verbose >= 2 {
            let styled = format!("{} {}", "".yellow(), message);
            self.write(message, Some(&styled));
        }
    }

    pub fn warning(&mut self, message: &str) {
        if self.verbose >= 1 {
            let styled = format!("{} {}", "".yellow(), message);
            self.write(message, Some(&styled));
        }
    }

    pub fn warn(&mut self, message: &str) {
        self.warning(message);
    }

    /// Create a progress bar for brute force candidate generation
    pub fn create_brute_progress(&self, total: u64, message: &str) -> ProgressBar {
        let pb = ProgressBar::new(total);
        pb.set_style(
            ProgressStyle::default_bar()
                .template("{spinner:.cyan} {msg} {bar:40.cyan/blue} {pos}/{len} ({percent}%)")
                .unwrap()
                .progress_chars("█▉▊▋▌▍▎▏  "),
        );
        pb.set_message(message.to_string());
        pb
    }

    /// Create a progress bar for verification
    pub fn create_verify_progress(&self, total: u64) -> ProgressBar {
        let pb = ProgressBar::new(total);
        pb.set_style(
            ProgressStyle::default_bar()
                .template("{spinner:.green} {msg} {bar:40.green/yellow} {pos}/{len} ({percent}%) | {elapsed_precise} | ETA: {eta_precise}")
                .unwrap()
                .progress_chars("█▉▊▋▌▍▎▏  "),
        );
        pb.set_message("Verifying candidates".to_string());
        pb
    }
}