utls 0.8.7

A simple utilities library for stuff I actually use sometimes, with a large focus on convenience and lack of dependencies.
Documentation
use std::io::{self, Write, BufRead};
use std::thread;
use std::time::Duration;
use utls::{prog::{PBStyle, PB, Spinner}, traits::decl::Empty};

fn main() {
    let mut pb = PB::EMPTY;
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    let mut input = String::new();

    // Basic Configuration
    print!("Enter maximum value for the progress bar: ");
    stdout.flush().unwrap();
    stdin.lock().read_line(&mut input).unwrap();
    pb.max = input.trim().parse().unwrap_or(100);

    // Style Selection
    println!("\nAvailable styles:");
    println!("1. Classic  ([####    ] 40%)");
    println!("2. Modern   (│████░░░░│ 40/100 (40%))");
    println!("3. Minimal  (====>---- 40%)");
    println!("4. Fancy    (『■■■□□□』40/100 • 40% • 0:00:04.123)");
    println!("5. ASCII    ((***...) 40%)");
    println!("6. Dots     (⣿⣿⣿⠒⠒⠒ 40%)");
    println!("7. Arrows   (◀▶▶▶▶▷▷▷▷◀▶ 40%)");
    println!("8. Box Heavy(┣████░░░░┫ 40/100 (40%))");
    print!("Choose style (1-8): ");
    stdout.flush().unwrap();
    
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    pb.style = match input.trim() {
        "2" => PBStyle::modern(),
        "3" => PBStyle::minimal(),
        "4" => PBStyle::fancy(),
        "5" => PBStyle::ascii(),
        "6" => PBStyle::dots(),
        "7" => PBStyle::arrows(),
        "8" => PBStyle::box_heavy(),
        _ => PBStyle::classic(),
    };

    // Advanced Configuration
    println!("\n=== Advanced Configuration ===");

    // Width
    print!("Enter bar width (10-100): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    pb.conf.width = input.trim().parse().unwrap_or(50).clamp(10, 100);

    // Update Frequency
    print!("Enter update delay in milliseconds (50-1000): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    let delay_ms = input.trim().parse().unwrap_or(100).clamp(50, 1000);
    pb.conf.update_freq = Duration::from_millis(delay_ms as u64);

    // Message Format
    print!("Enter message format (leave empty for default): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    let message = input.trim().to_string();
    if !message.is_empty() {
        pb.style.message = message;
    }

    // Description
    print!("Enter description (leave empty for none): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    let desc = input.trim().to_string();
    if !desc.is_empty() {
        pb.style.desc = desc;
    }

    // Spinner
    print!("Add spinner? (y/N): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    if input.trim().eq_ignore_ascii_case("y") {
        print!("Enter spinner characters (e.g., '|/-\\'): ");
        stdout.flush().unwrap();
        input.clear();
        stdin.lock().read_line(&mut input).unwrap();
        let spinner_chars = input.trim();
        if !spinner_chars.is_empty() {
            pb.style.spinner = Some(Spinner::new(spinner_chars));
            
            print!("Enter spinner update frequency in ms (50-1000): ");
            stdout.flush().unwrap();
            input.clear();
            stdin.lock().read_line(&mut input).unwrap();
            let spinner_freq = input.trim().parse().unwrap_or(100).clamp(50, 1000);
            pb.conf.spinner_update_freq = Duration::from_millis(spinner_freq);
        }
    }

    // Precision
    print!("Enter decimal precision for percentages (0-5): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    pb.style.prec = input.trim().parse().unwrap_or(0).clamp(0, 5);

    // Bar End Character
    print!("Enter left bar end character (leave empty for default): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    let bar_end = input.trim().to_string();
    if !bar_end.is_empty() {
        pb.style.bar_end = bar_end;
    }

    // Advanced Features
    print!("Enable advanced statistics? (y/N): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    pb.conf.stats.adv_enabled = input.trim().eq_ignore_ascii_case("y");

    print!("Enable update deferral? (y/N): ");
    stdout.flush().unwrap();
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();
    pb.conf.defer_updates = input.trim().eq_ignore_ascii_case("y");

    // Enable formatting and bar display
    pb.conf.formatting = true;
    pb.conf.bar = true;

    // Print the equivalent builder code
    println!("\nEquivalent builder code:");
    println!("let pb = PB::builder({})", pb.max);
    println!("    .style(PBStyle::{}())", match pb.style {
        _ if pb.style.endcaps == PBStyle::modern().endcaps => "modern",
        _ if pb.style.endcaps == PBStyle::minimal().endcaps => "minimal",
        _ if pb.style.endcaps == PBStyle::fancy().endcaps => "fancy",
        _ if pb.style.endcaps == PBStyle::ascii().endcaps => "ascii",
        _ if pb.style.endcaps == PBStyle::dots().endcaps => "dots",
        _ if pb.style.endcaps == PBStyle::arrows().endcaps => "arrows",
        _ if pb.style.endcaps == PBStyle::box_heavy().endcaps => "box_heavy",
        _ => "classic",
    });
    println!("    .width({})", pb.conf.width);
    println!("    .update_frequency(Duration::from_millis({}))", delay_ms);
    if !pb.style.message.is_empty() {
        println!("    .message(\"{}\")", pb.style.message);
    }
    if !pb.style.desc.is_empty() {
        println!("    .description(\"{}\")", pb.style.desc);
    }
    if let Some(ref spinner) = pb.style.spinner {
        println!("    .spinner_chars(\"{}\")", spinner.chars.iter().collect::<String>());
        println!("    .spinner_freq(Duration::from_millis({}))", 
            pb.conf.spinner_update_freq.as_millis());
    }
    if pb.style.prec > 0 {
        println!("    .precision({})", pb.style.prec);
    }
    if !pb.style.bar_end.is_empty() {
        println!("    .bar_end(\"{}\")", pb.style.bar_end);
    }
    if pb.conf.stats.adv_enabled {
        println!("    .adv_stats(true)");
    }
    if pb.conf.defer_updates {
        println!("    .defer(true)");
    }
    println!("    .build();");

    println!("\nPress Enter to start the progress bar...");
    input.clear();
    stdin.lock().read_line(&mut input).unwrap();

    // Start the progress bar
    let (handle, pb_arc) = pb.threaded_start();

    // Simulate progress
    while let Ok(mut guard) = pb_arc.lock() {
        if guard.current >= guard.max {
            break;
        }
        guard.inc();
        drop(guard);
        thread::sleep(pb.conf.update_freq);
    }

    handle.join().unwrap();
}