shelly-shell 0.1.0

A Rust based Unix style shell with a typed and structured language syntax.

use std::collections::{ HashMap, VecDeque };

use crate::language::data::value::Value;


#[derive(Clone, PartialEq, Eq)]
pub enum ValueVisibility
{
    Private,
    Exported
}


#[derive(Clone)]
pub struct ScopedValue
{
    pub value: Value,
    pub exported: ValueVisibility
}


pub struct ScopedVariables
{
    scopes: VecDeque<HashMap<String, ScopedValue>>,
}


impl ScopedVariables
{
    pub fn new_from_environment() -> Self
    {
        let mut variables = HashMap::new();

        for (name, value) in std::env::vars()
        {
            let scoped_value = ScopedValue
                {
                    value: Value::String(value),
                    exported: ValueVisibility::Exported
                };

            variables.insert(format!("${}", name), scoped_value);
        }

        let mut scopes = VecDeque::new();

        scopes.push_back(variables);

        Self
        {
            scopes
        }
    }

    pub fn push_scope(&mut self)
    {
        self.scopes.push_back(HashMap::new());
    }

    pub fn pop_scope(&mut self)
    {
        if self.scopes.len() == 1
        {
            panic!("Cannot pop the global scope!");
        }

        self.scopes.pop_back();
    }

    pub fn create(&mut self, name: String, value: ScopedValue) -> Result<(), String>
    {
        if let Some(scope) = self.scopes.back_mut()
        {
            scope.insert(name, value);
            Ok(())
        }
        else
        {
            Err("No scope available to create the variable.".to_string())
        }
    }

    pub fn get(&self, name: &str) -> Option<&ScopedValue>
    {
        for scope in self.scopes.iter().rev()
        {
            if let Some(value) = scope.get(name)
            {
                return Some(value);
            }
        }

        None
    }

    pub fn get_mut(&mut self, name: &str) -> Option<&mut ScopedValue>
    {
        for scope in self.scopes.iter_mut().rev()
        {
            if let Some(value) = scope.get_mut(name)
            {
                return Some(value);
            }
        }

        None
    }

    pub fn names(&self) -> impl Iterator<Item = &str>
    {
        self.scopes.iter().flat_map(|scope| scope.keys().map(String::as_str))
    }

    pub fn get_all_flattened(&self) -> HashMap<String, ScopedValue>
    {
        let mut flattened = HashMap::new();

        for scope in self.scopes.iter()
        {
            for (name, value) in scope.iter()
            {
                flattened.insert(name.clone(), value.clone());
            }
        }

        flattened
    }

    pub fn current_scope(&self) -> usize
    {
        self.scopes.len() - 1
    }

    pub fn reset_to_scope(&mut self, scope_index: usize)
    {
        while self.scopes.len() > scope_index + 1
        {
            self.scopes.pop_back();
        }
    }
}