ytest 0.1.0

Ygens testing infrastructure
use core::str;
use std::{fs::File, io::{Read, Write}};
use std::process::{exit, Command};

use Ygen::Support::{Cli, Colorize};

mod parse;
use parse::*;

fn main() {
    let mut cli = Cli::new(
        "ytest", "The testing tool for ygen", "1.0", "Cr0a3"
    );
    
    cli.add_opt("h", "help", "Displays help");
    cli.add_opt("v", "version", "Displays the version");

    cli.add_opt("no-exit", "no-exit-on-error", "Ytest does not quite when an error occurs");
    cli.add_opt("neg-exit", "exit-code-neg", "Ytest exits automaticly even with `no-exit` if the programm returned with code -1");

    cli.add_arg("t", "test", "The file to the testcase", true);

    cli.scan();

    if cli.opt("h") {
        cli.help();
    } else if cli.opt("v") {
        cli.version();
    }

    let file = cli.arg_val("t").expect("We said it was required");
    let mut file = match File::open(file) {
        Ok(f) => f,
        Err(err) => {
            println!("{}: {}", "Error".red().bold(), err);
            exit(-1)
        },
    };

    let mut buf = String::new();
    match file.read_to_string(&mut buf) {
        Ok(_) => {},
        Err(err) => {
            println!("{}: {}", "Error".red().bold(), err);
            exit(-1)
        },
    };

    let parsed = parse(buf);

    let path_str = "./tmp.yl";

    let path = std::path::PathBuf::from(path_str);

    if path.exists() {
        let _ = std::fs::remove_file(&path);
    }

    if let Some(input) = parsed.input {
        let mut file = match File::options().write(true).create(true).open(&path) {
            Ok(file) => file,
            Err(err) => {
                println!("{}: {}", "Error".red().bold(), err);
                exit(-1)
            },
        };
    
        match file.write_all(input.as_bytes()) {
            Ok(_) => {},
            Err(err) => {
                println!("{}: {}", "Error".red().bold(), err);
                exit(-1)
            },
        }
    }

    let mut found = String::new();
    let mut found_stderr = String::new();

    let mut code = 0;

    for cmd in parsed.cmd {
        let args = cmd.replace("%s", path_str);
        let args = unescaper::unescape(&args).unwrap();
        let args = args.trim();
        let mut args = args.split(" ").collect::<Vec<&str>>();

        let program = args.get(0).expect("expected valid excutable name").to_string();

        args.reverse();
        args.pop();
        args.reverse();


        println!("{}: executing following commandline: '{}{}'", "Info".blue().bold(), program, {
            let mut fmt = String::new();

            for arg in &args {
                fmt.push_str(&format!(" {}", arg));
            }

            fmt
        });

        let mut cmd = Command::new( program );
        
        for arg in args {
            if arg == "" {
                break;
            }

            if arg == " " {
                continue;
            }

            cmd.arg( arg );
        }

        let out = cmd.output().expect("failed to execute the process");
        let stdout = out.stdout;

        let stdout = str::from_utf8(&stdout).unwrap();
        let stdout = stdout.chars().filter(|x| !x.is_whitespace()).collect::<String>();

        found.push_str(&stdout);

        let stderr = out.stderr;

        let stderr = str::from_utf8(&stderr).unwrap();
        let stderr = stderr.chars().filter(|x| !x.is_whitespace()).collect::<String>();

        found_stderr.push_str( &stderr );

        match cmd.status() {
            Ok(status) => {
                if !status.success() {
                    if let Some(exit_code) = status.code() {
                        code = exit_code as u32;

                        if let Some(expected_code) = parsed.expected_code {
                            if expected_code == 0 || (cli.opt("exit") && code == (-1i32 as u32)) && !parsed.ignore_fail {
                                println!("{}: the programm didn't exit sucessfull with code {}", "Error".red().bold(), exit_code);
                                if !cli.opt("no-exit") {
                                    exit(-1);
                                }
                            }
                        } else if cli.opt("neg-exit") && code == (-1i32 as u32) && !parsed.ignore_fail {
                            println!("{}: the programm didn't exit sucessfull with code {}", "Error".red().bold(), exit_code);
                            exit(-1);
                        }
                    } else if !parsed.ignore_fail {
                        println!("{}: the programm didn't exit sucessfull", "Error".red().bold());
                        if !cli.opt("no-exit") {
                            exit(-1)
                        }
                    }
                }
            },
            Err(err) => {
                println!("{}: {}", "Error".red().bold(), err);
                if !cli.opt("no-exit") {
                    exit(-1)
                }
            }
        };
    }

    let _ = std::fs::remove_file(&path);

    if let Some(expected) = parsed.expected_out {
        if expected != found {
            println!("{}: expected output didn't match actual output", "Error".red().bold());
            println!("found:    {:?}", found);
            println!("expected: {:?}", expected);
            if !cli.opt("no-exit") {
                exit(-1)
            }
        }
    }
    if let Some(expected) = parsed.expected_stderr {
        if expected != found_stderr {
            println!("{}: expected stderr didn't match actual output", "Error".red().bold());
            println!("found:    {:?}", found);
            println!("expected: {:?}", expected);
            if !cli.opt("no-exit") {
                exit(-1)
            }
        }
    }

    if let Some(expected_code) = parsed.expected_code {
        if expected_code as u32 != code {
            println!("{}: expected exit code: {} found {}", "Error".red().bold(), expected_code, code);
            if !cli.opt("no-exit") {
                exit(-1)
            }
        } else {
            println!("expected exit code {} matched with found one {}", expected_code, code);
        }
    }
}