instruct 0.1.0

A language to write general purpose 'makefile like' tasks which are powerful and reuseable
Documentation
use clap::Parser;
use log::error;
use thiserror::Error;

pub mod cli;
pub mod config;
pub mod interpreter;
pub mod logger;
pub mod parse;

#[derive(Error, Debug)]
pub enum TaskLangError {
    #[error(
        "Error while loading configuration (make sure the task.toml is valid!){}", print_err(.0)
    )]
    ConfigError(anyhow::Error),
    #[error("Error while parsing module '{0}' at '{1}'{}", print_err(.2))]
    ParserError(String, String, anyhow::Error),
    #[error("Error while adding module '{0}' at '{1}'{}", print_err(.2))]
    NamespaceError(String, String, anyhow::Error),
    #[error("Error while resolving task '{0}'{}", print_err(.1))]
    ResolveError(String, anyhow::Error),
    #[error("Error while analising task '{0}'{}", print_err(.1))]
    StaticAnalysisError(String, anyhow::Error),
    #[error("Error while executing task '{0}'{}", print_err(.1))]
    ExecutionError(String, anyhow::Error),
}

fn print_err(error: &anyhow::Error) -> String {
    let mut repr: String = "\ncaused by:\n\n".into();
    for (counter, source) in error.chain().enumerate() {
        repr += &format!("\t#{}: {}\n", counter, source);
    }
    repr
}

pub fn run() -> Result<(), TaskLangError> {
    let cli = cli::Cli::parse();
    let config = config::Config::load(cli.task_file).map_err(TaskLangError::ConfigError)?;

    logger::setup_logger(&cli.log_level).unwrap();

    let task = &cli.task;

    let mut root_namespace = interpreter::RootNamespace::default();

    for (name, module) in &config.module {
        let location = &module.location;
        let namespace = parse::load_and_parse(location)
            .map_err(|err| TaskLangError::ParserError(name.into(), location.into(), err))?;
        root_namespace
            .add_root(namespace)
            .map_err(|err| TaskLangError::NamespaceError(name.into(), location.into(), err))?;
    }

    let mut interpreter = interpreter::Interpreter::new(root_namespace);
    interpreter
        .resolve(task)
        .map_err(|err| TaskLangError::ResolveError(task.into(), err))?;
    interpreter
        .initialize()
        .map_err(|err| TaskLangError::StaticAnalysisError(task.into(), err))?;
    interpreter
        .run()
        .map_err(|err| TaskLangError::ExecutionError(task.into(), err))?;

    Ok(())
}