xasm 0.5.10

Compiler for the xasm programming language
Documentation
use clap::{clap_app, crate_version, AppSettings::ArgRequiredElseHelp, ArgMatches};
use std::fs::read_to_string;
use xasm::compile::Compile;
use xassembler::{Golang, Rust};

fn main() -> Result<(), String> {
    let matches = clap_app!(xasm =>
        (version: crate_version!())
        (author: "Adam McDaniel <adam.mcdaniel17@gmail.com>")
        (about: "Compiler for the xasm programming language")
        (@group target +required =>
            (@arg golang: -g --go "Compile to Golang")
            (@arg rust: -r --rs "Compile to Rust")
        )
        (@subcommand run =>
            (version: "0.1")
            (about: "Run a xasm file")
            (author: "Adam McDaniel <adam.mcdaniel17@gmail.com>")
            (@arg INPUT: +required "Input file")
            (@arg PACKAGES: -p --packages +takes_value ... "Paths to foreign packages to use")
        )
        (@subcommand compile =>
            (version: "0.1")
            (about: "Compile a xasm file")
            (author: "Adam McDaniel <adam.mcdaniel17@gmail.com>")
            (@arg INPUT: +required "Input file")
            (@arg OUTPUT: -o --output +takes_value default_value("a.out") "Output path to executable")
            (@arg PACKAGES: -p --packages +takes_value ... "Paths to foreign packages to use")
        )
    )
    .setting(ArgRequiredElseHelp)
    .get_matches();

    if matches.is_present("golang") {
        cli::<Golang>(matches)?
    } else if matches.is_present("rust") {
        cli::<Rust>(matches)?
    }

    Ok(())
}

fn cli<T: Compile>(matches: ArgMatches) -> Result<(), String> {
    if let Some(matches) = matches.subcommand_matches("run") {
        let input = matches.value_of("INPUT").unwrap();

        let packages = match matches.values_of("PACKAGES") {
            Some(list) => list.map(|s| s.trim_matches('/')).collect::<Vec<&str>>(),
            None => vec![]
        };

        if let Ok(contents) = read_to_string(input) {
            match T::assemble(&contents) {
                Ok(compiled) => T::run_subcommand(&compiled, packages)?,
                Err(e) => println!("{}", e),
            }
        } else {
            eprintln!("Could not open file {}", input);
        }
    } else if let Some(matches) = matches.subcommand_matches("compile") {
        let input = matches.value_of("INPUT").unwrap();
        let output = matches.value_of("OUTPUT").unwrap();

        let packages = match matches.values_of("PACKAGES") {
            Some(list) => list.map(|s| s.trim_matches('/')).collect::<Vec<&str>>(),
            None => vec![]
        };
        
        if let Ok(contents) = read_to_string(input) {
            match T::assemble(&contents) {
                Ok(compiled) => T::compile_subcommand(&compiled, packages, &output)?,
                Err(e) => println!("{}", e),
            }
        } else {
            eprintln!("Could not open file {}", input);
        }
    }

    Ok(())
}