readmdict 0.1.0

A Rust implementation for reading MDict dictionary files (.mdx format)
Documentation
use std::path::Path;
use std::process;
use rust_readmdict::readmdict::Mdd;

fn main() {
    let args: Vec<String> = std::env::args().collect();
    
    // Parse command line arguments
    if args.len() < 2 {
        eprintln!("Usage: cargo run --example show_mdd <mdd_file>");
        eprintln!("Analyze and display contents of MDD files");
        process::exit(1);
    }
    
    let mdd_file = &args[1];

    // Check if MDD file exists
    if !Path::new(mdd_file).exists() {
        eprintln!("Error: MDD file '{}' not found", mdd_file);
        process::exit(1);
    }

    // Load MDD file
    match load_and_analyze_mdd(mdd_file) {
        Ok(_) => {},
        Err(e) => {
            eprintln!("Error: {}", e);
            if e.to_string().to_lowercase().contains("lzo") {
                eprintln!("\nThis error is likely related to missing LZO compression support.");
                eprintln!("Please install the LZO library.");
            }
            process::exit(1);
        }
    }
}

fn load_and_analyze_mdd(mdd_file: &str) -> Result<(), Box<dyn std::error::Error>> {
    println!("Loading MDD file: {}", mdd_file);
    
    let mdd = Mdd::new(mdd_file, None)?;
    
    // Convert MDD items to a vector for easier handling
    let mdd_items: Vec<(Vec<u8>, Vec<u8>)> = mdd.items()?;
    println!("Successfully loaded MDD file with {} items", mdd_items.len());
    println!("{}", "=".repeat(70));

    // Categorize files by type
    let mut image_files: Vec<(String, &Vec<u8>)> = Vec::new();
    let mut css_files: Vec<(String, &Vec<u8>)> = Vec::new();
    let mut js_files: Vec<(String, &Vec<u8>)> = Vec::new();
    let mut audio_files: Vec<(String, &Vec<u8>)> = Vec::new();
    let mut other_files: Vec<(String, &Vec<u8>)> = Vec::new();

    for (filename, content) in &mdd_items {
        let filename_str = String::from_utf8_lossy(filename);
        let filename_lower = filename_str.to_lowercase();
        
        if filename_lower.ends_with(".jpg") || filename_lower.ends_with(".png") || 
           filename_lower.ends_with(".gif") || filename_lower.ends_with(".bmp") || 
           filename_lower.ends_with(".jpeg") {
            image_files.push((filename_str.to_string(), content));
        } else if filename_lower.ends_with(".css") {
            css_files.push((filename_str.to_string(), content));
        } else if filename_lower.ends_with(".js") {
            js_files.push((filename_str.to_string(), content));
        } else if filename_lower.ends_with(".wav") || filename_lower.ends_with(".mp3") || 
                  filename_lower.ends_with(".ogg") || filename_lower.ends_with(".m4a") {
            audio_files.push((filename_str.to_string(), content));
        } else {
            other_files.push((filename_str.to_string(), content));
        }
    }

    // Display summary
    println!("File type summary:");
    println!("  Images: {}", image_files.len());
    println!("  CSS files: {}", css_files.len());
    println!("  JavaScript files: {}", js_files.len());
    println!("  Audio files: {}", audio_files.len());
    println!("  Other files: {}", other_files.len());
    println!();

    // Show first 10 image files
    if !image_files.is_empty() {
        println!("First 10 image files:");
        for (i, (filename, content)) in image_files.iter().take(10).enumerate() {
            println!("  {}. {} ({} bytes)", i + 1, filename, content.len());
        }
        println!();
    }

    // Show all CSS files (if any)
    if !css_files.is_empty() {
        println!("CSS files:");
        for (i, (filename, content)) in css_files.iter().enumerate() {
            match std::str::from_utf8(content) {
                Ok(css_text) => {
                    println!("  {}. {} ({} characters)", i + 1, filename, css_text.len());
                    let preview = if css_text.len() > 200 {
                        format!("{}...", &css_text[..200])
                    } else {
                        css_text.to_string()
                    };
                    println!("     Preview: {}", preview);
                }
                Err(_) => {
                    println!("  {}. {} ({} bytes) [Binary]", i + 1, filename, content.len());
                }
            }
        }
        println!();
    }

    // Show all JavaScript files (if any)
    if !js_files.is_empty() {
        println!("JavaScript files:");
        for (i, (filename, content)) in js_files.iter().enumerate() {
            match std::str::from_utf8(content) {
                Ok(js_text) => {
                    println!("  {}. {} ({} characters)", i + 1, filename, js_text.len());
                    let preview = if js_text.len() > 200 {
                        format!("{}...", &js_text[..200])
                    } else {
                        js_text.to_string()
                    };
                    println!("     Preview: {}", preview);
                }
                Err(_) => {
                    println!("  {}. {} ({} bytes) [Binary]", i + 1, filename, content.len());
                }
            }
        }
        println!();
    }

    // Show first 5 audio files
    if !audio_files.is_empty() {
        println!("First 5 audio files:");
        for (i, (filename, content)) in audio_files.iter().take(5).enumerate() {
            println!("  {}. {} ({} bytes)", i + 1, filename, content.len());
        }
        println!();
    }

    // Show first 10 other files
    if !other_files.is_empty() {
        println!("First 10 other files:");
        for (i, (filename, content)) in other_files.iter().take(10).enumerate() {
            match std::str::from_utf8(content) {
                Ok(text_content) => {
                    let trimmed = text_content.trim();
                    if !trimmed.is_empty() && trimmed.chars().take(100).all(|c| c.is_ascii()) {
                        println!("  {}. {} (text, {} characters)", i + 1, filename, text_content.len());
                        let preview = if text_content.len() > 200 {
                            format!("{}...", &text_content[..200])
                        } else {
                            text_content.to_string()
                        };
                        println!("     Preview: {}", preview);
                    } else {
                        println!("  {}. {} ({} bytes) [Binary]", i + 1, filename, content.len());
                    }
                }
                Err(_) => {
                    println!("  {}. {} ({} bytes) [Binary]", i + 1, filename, content.len());
                }
            }
        }
        println!();
    }

    println!("MDD file analysis complete.");
    Ok(())
}