finn-assembler 0.2.2

The Finn IR Assembler
use std::fs;
use std::path::Path;
use std::process::Command;

use fs_extra::dir::*;


use crate::error::*;
use crate::stdout::*;


const OUTPUT_CRATE: &str = "out";
const CARGO_TOML: &str = "[package]
name = \"output\"
version = \"0.1.0\"
authors = [\"adam-mcdaniel <adam.mcdaniel17@gmail.com>\"]
edition = \"2018\"


[profile.release]
opt-level = 3
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 16
panic = 'unwind'
incremental = false
overflow-checks = false

[dependencies]
# hlvm = {path=\"../hlvm\"}
hlvm = \"0.10.0\"

num-traits = \"*\"
";


pub fn path_to_str(name: &str) -> String {
    match Path::new(&name.clone().to_string()).file_name() {
        Some(p) => {
            match p.to_str() {
                Some(os_str) => os_str.to_string(),
                None => {
                    sub_error(
                        &format!("{} is an invalid path", name)
                    );
                    throw();
                    "".to_string()
                }
            }
        },
        None => {
            sub_error(
                &format!("{} is an invalid path", name)
            );
            throw();
            "".to_string()
        }
    }
}


pub fn read_file(file: &str) -> String {
    match fs::read_to_string(file) {
        Ok(k) => k,
        Err(_) => {
            sub_error(
                &format!("Unable to read file '{}'", &file)
            );
            throw();
            "".to_string()
        }
    }
}


fn copy_folder(folder_path: &str, new_folder_path: &str) {
    sub_info(
        &format!("Including crate '{}'...", path_to_str(&folder_path))
    );

    let mut options = CopyOptions::new();
    options.copy_inside = true;

    match remove(new_folder_path) {_ => {}};

    match copy(folder_path, new_folder_path, &options) {
        Ok(_) => {
            sub_info(&format!("Successfully included crate '{}'", path_to_str(&folder_path)));
        },
        Err(e) => {
            sub_error(
                &format!("Unable to copy folder '{}' to '{}' because '{}'", &folder_path, &new_folder_path, e)
            );
            throw();
        }
    };
}


fn make_output_crate() {
    sub_info(
        &format!("Creating output crate")
    );
    if Path::new(OUTPUT_CRATE).exists() {
        sub_info(
            &format!("Output crate already exists")
        );
        
    } else {
        match Command::new("cargo")
            .current_dir(".")
            .arg("new")
            .arg(OUTPUT_CRATE)
            .output()
            {
            Ok(_) => {
                sub_info(
                    &format!("Created output crate")
                );
            },
            Err(_) => {
                sub_error(
                    &format!("Could not create output crate; do you have Rust installed? You can install it with this command: `curl https://sh.rustup.rs -sSf | sh`")
                );
                throw();
            }
        };

    }
}


fn write_to_file(file: String, contents: String) {
    match fs::write(&file, &contents) {
        Ok(_) => {
            sub_info(
                &format!("Wrote '{}...' to file '{}'", &contents[0..9], file)
            );
        },
        Err(e) => {
            sub_error(&format!("Could not write to file '{}' because {}", file, e));
            throw();
        }
    }
}


pub fn write_deps(crate_names: Vec<String>) {
    info(&format!("Including dependant crates..."));
    make_output_crate();

    let mut dependencies = "".to_string();

    for name in crate_names {
        let dep_name = path_to_str(&name);
        dependencies += &format!("{} = {{path=\"{}\"}}\n", dep_name, dep_name);

        copy_folder(&name, &(OUTPUT_CRATE.to_string() + "/" + &dep_name));
    }

    write_to_file(
        OUTPUT_CRATE.to_string() + "/Cargo.toml",
        CARGO_TOML.to_string() + &dependencies,
    );

    if has_thrown_error() {
        error(
            &format!("Could not include dependant crates due to errors")
        );
        stop();
    } else {
        success("Successfully included dependant crates");
    }
}


pub fn write_compiled_script(script: String) {
    info(&format!("Writing compiled script to output crate"));
    write_to_file(OUTPUT_CRATE.to_string() + "/src/main.rs", script);

    if has_thrown_error() {
        error(
            &format!("Could not write compiled script due to errors")
        );
        stop();
    } else {
        success("Successfully wrote compiled script");
    }
}
    


pub fn compile_output_crate() {
    info(&format!("Compiling output crate"));
    match Command::new("cargo")
            .current_dir(OUTPUT_CRATE)
            .arg("build")
            .arg("--release")
            .output() {

        Ok(k) => {
            if k.status.success() {
                sub_info("Built output crate");
            } else {
                sub_error("Could not compile output crate; did you include all dependant crates?");
                throw();
            }
        },
        Err(_) => {
            sub_error("Failed start cargo");
            throw();
        }
    }
        
    if has_thrown_error() {
        error(
            &format!("Could not compile output crate due to errors")
        );
        stop();
    } else {
        success("Successfully compiled output crate");
    }
}