rs-asm6805 1.0.0

6805 Datalink Assembler in Rust
Documentation
use clap::{Arg, Command};
use rs_asm6805::{assemble, expand_tabs};
use std::fs;
use std::path::Path;

fn main() {
    let matches = Command::new("asm6805")
        .about("6805 Datalink Assembler")
        .version("1.0.0")
        .arg(
            Arg::new("input")
                .help("Input assembly file")
                .required(true)
                .index(1),
        )
        .arg(
            Arg::new("output")
                .short('o')
                .long("output")
                .value_name("FILE")
                .help("Output hex file (default: input.hex)")
        )
        .arg(
            Arg::new("listing")
                .short('l')
                .long("listing")
                .value_name("FILE")
                .help("Generate listing file")
        )
        .get_matches();

    let input_file = matches.get_one::<String>("input").unwrap();
    
    // Read input file
    let source_content = match fs::read_to_string(input_file) {
        Ok(content) => content,
        Err(e) => {
            eprintln!("Error reading input file '{}': {}", input_file, e);
            std::process::exit(1);
        }
    };

    // Split into lines and expand tabs
    let source_lines: Vec<String> = source_content
        .lines()
        .map(|line| expand_tabs(line, 8))
        .collect();

    // Assemble
    match assemble(input_file.to_string(), source_lines.clone()) {
        Ok((errors, hex_output, listing)) => {
            // Print errors to stderr if any
            if !errors.is_empty() {
                for error in &errors {
                    eprintln!("{}", error);
                }
            }

            // Determine output filename
            let output_file = if let Some(output) = matches.get_one::<String>("output") {
                output.clone()
            } else {
                let input_path = Path::new(input_file);
                input_path.with_extension("hex").to_string_lossy().to_string()
            };

            // Write hex output
            if let Err(e) = fs::write(&output_file, &hex_output) {
                eprintln!("Error writing output file '{}': {}", output_file, e);
                std::process::exit(1);
            }

            println!("Assembly completed successfully.");
            println!("Output written to: {}", output_file);

            // Generate listing if requested
            if let Some(listing_file) = matches.get_one::<String>("listing") {
                let listing_content = listing.join("\n");
                if let Err(e) = fs::write(listing_file, listing_content) {
                    eprintln!("Error writing listing file '{}': {}", listing_file, e);
                    std::process::exit(1);
                }
                println!("Listing written to: {}", listing_file);
            }

            // Exit with error code if there were assembly errors
            if !errors.is_empty() {
                std::process::exit(1);
            }
        }
        Err(errors) => {
            eprintln!("Assembly failed with {} error(s):", errors.len());
            for error in &errors {
                eprintln!("{}", error);
            }
            std::process::exit(1);
        }
    }
}