crab_env 0.1.11

Environment lib of the Crab_shell crate, an alternative Rust-like cross-platform shell
Documentation
//! Provides utilities for developing Crates for the Crab-Shell.
//! 
//! The most useful one is the Environment struct,  
//! 
//! 
//! 
//!
//! 

use colored::Colorize;
use once_cell::sync::Lazy;

/// Struct that contains all Crab-Shell variables.
#[allow(dead_code)]
#[derive(serde_derive::Deserialize, serde_derive::Serialize, Debug, Clone)]
pub struct Environment {
    /// Same as `std::env::current_dir`, used to modify the current directory of the shell from a Crate
    pub current_dir: String,

    /// Extra directories the shell will look for Crates
    pub crate_dirs: Vec<String>,

    /// Vector of all the global variables of the shell. 
    /// 
    /// It is saved between executions.
    pub global_vars: Vec<toml::Value>,

    /// Return value the shell will use when using a Crate as an argument for another Crate.
    /// 
    /// It is sent to the other Crate as an arg, so it's the developer's job to parse it correctly.
    /// # Example: 
    /// 
    /// Shell input: `cd(location(cd))`
    /// ```
    /// // Returns the path of a Crate
    /// fn location() {
    ///     let env = crab_env::load();
    ///     
    /// 
    /// }
    /// 
    /// 
    ///
    /// 
    /// ```
    /// 
    pub return_val: String,

    pub debug: bool,
}

#[allow(dead_code)]
impl Environment {
    // Creates a new Environment with empty values.
    pub fn default() -> Self {
        Self { current_dir: String::new(), crate_dirs: Vec::new(), global_vars: Vec::new(), return_val: String::new(), debug: false }
    }

    /// Loads the current Environment from CONFIG_PATH/env.toml and returns it.
    pub fn load() -> Environment {
        toml::from_str(
            &std::fs::read_to_string(ENV_PATH.as_path())
            .unwrap_or(String::new()))
            .unwrap_or(Environment::default())
    }

    /// Loads the current Environment from CONFIG_PATH/env.toml into the provided env, overwriting it.
    pub fn load_into(&mut self) {
        let temp = Environment::load();
        self.current_dir = temp.current_dir;
        self.global_vars = temp.global_vars;
        self.return_val = temp.return_val;
        self.debug = temp.debug;
    }

    /// Saves Environment into CONFIG_PATH/env.toml
    pub fn save(&self) {
        std::fs::write(ENV_PATH.as_path(), toml::to_string(self).unwrap_or(ENV_DEFAULT.into())).unwrap();
    }

    /// Assigns `value` to [return_val](`struct.Environment.return_val`) and [saves](`save`) it to 
    pub fn return_and_save(&mut self, value: &str) {
        self.return_val = value.into();
        Environment::save(self);
    }
}

/// Loads the current Environment from CONFIG_PATH/env.toml and returns it.
pub fn load() -> Environment {
    Environment::load()
}

/// Loads the current Environment from `CONFIG_PATH/env.toml` into the provided env, overwriting it.
pub fn load_into(env: &mut Environment) {
    Environment::load_into(env)
}

/// Saves Environment into `CONFIG_PATH/env.toml`
/// 
/// Panics if `CONFIG_PATH/env.toml` cannot be accessed.
pub fn save(env: &Environment) {
    Environment::save(env)
}

/// Prints a bold orange message
pub fn print_message(msg: &str) {
    println!("{}", msg.bold().color(ORANGE))
}

pub const ORANGE: colored::Color = colored::Color::TrueColor {
    r: 255,
    g: 127,
    b: 80,
};

/// Returns Crab-Shell's main directories paths (CONFIG and DATA).
pub static PROJECT_DIRS: Lazy<directories::ProjectDirs> = Lazy::new(|| 
    match directories::ProjectDirs::from("com", "", "crab") {
        None => {
            println!("Unable to write to home directory, maybe you don't have permissions?");
            std::process::exit(1);
        }
        Some(v) => {
            v
        }
    });
pub static USER_DIRS: Lazy<directories::UserDirs> = Lazy::new(|| 
    match directories::UserDirs::new(){
        None => {
            println!("Unable to access the home directory, maybe you don't have permissions?");
            std::process::exit(1);
        }
        Some(v) => {
            v
        }
    });

pub const CONFIG_PATH: Lazy<&'static std::path::Path> = Lazy::new(|| PROJECT_DIRS.config_dir());

pub const ENV_DEFAULT: &'static str = include_str!("env.toml");
pub static ENV_PATH: Lazy<std::path::PathBuf> = Lazy::new(|| CRATES_PATH.join("env.toml"));

pub static CRATES_PATH: Lazy<std::path::PathBuf> = Lazy::new(|| PROJECT_DIRS.config_dir().join("crates"));

/// List of Crates currently installed.
pub static CRATES: Lazy<std::collections::HashSet<String>> = Lazy::new(|| {
    let mut crates = std::collections::HashSet::new();
    let dirs = match CRATES_PATH.read_dir() {
        Ok(p) => p,
        Err(_) => {
            let config = CONFIG_PATH.to_str().unwrap();
            println!("{}", "\nFirst time starting Crab (or the crab config folder was deleted).\n\nDownloading essential crates from https://github.com/LyonSyonII/Crab-Shell".bold().color(ORANGE));
            let git = std::process::Command::new("git")
                .args(["clone", "--depth", "1", "--filter=blob:none", "--no-checkout", "https://github.com/LyonSyonII/Crab-Shell", config]).status();
            if let Err(e) = git {
                println!("{} {}", "error: ".red(), e.to_string());
                println!("{}: Check if you have 'Git' installed, and if you have write permissions.", "error".red());
                std::process::exit(1);
            }

            std::env::set_current_dir(config).unwrap();

            let git = std::process::Command::new("git").args(["checkout", "master", "--", "crates"]).status();
            if let Err(e) = git {
                println!("{}: {}", "error: ".red(), e.to_string());
                std::process::exit(1);
            }

            println!("{}", "\nDownload completed, compiling Crates: ".bold().color(ORANGE));
            for cr in include_str!("core_crates.txt").split_whitespace() {
                println!("\n{}", cr.bold());
                std::process::Command::new("cargo")
                    .arg("build")
                    .arg("--release")
                    .arg(format!(
                        "--manifest-path={}/{}/Cargo.toml",
                        CRATES_PATH.to_string_lossy(),
                        cr
                    ))
                    .status()
                    .expect(&format!("Crate {} failed to build", cr));
            }

            CRATES_PATH.read_dir().unwrap()
        }
    };

    for file in dirs {
        if file.is_err() {
            break;
        }
        if file.as_ref().unwrap().file_type().unwrap().is_dir() {
            crates.insert(file.unwrap().file_name().to_string_lossy().into());
        }
    }

    for dir in load().crate_dirs {
        for file in std::fs::read_dir(dir).unwrap() {
            if file.is_err() {
                break;
            }
            let file = file.unwrap();
            if file.file_type().unwrap().is_dir() {
                crates.insert(file.file_name().to_string_lossy().into());
            }
        }
    }

    // Insert Built-ins
    crates.insert("exit".into());
    crates.insert("let ".into());
    crates.insert(":".into());
    crates.insert("=".into());
    crates.insert("$".into());

    crates
});