use crate::flash::env::Environment;
use crate::interpreter::Interpreter;
pub trait EnvironmentIntegration {
fn init_environment(&mut self);
fn get_env_var(&self, name: &str) -> Option<String>;
fn set_env_var(&mut self, name: &str, value: String);
fn export_var(&mut self, name: &str, value: Option<String>);
fn push_local_scope(&mut self);
fn pop_local_scope(&mut self);
fn set_positional_parameters(&mut self, params: Vec<String>);
fn update_exit_status(&mut self, status: i32);
}
impl EnvironmentIntegration for Interpreter {
fn init_environment(&mut self) {
let mut env = Environment::new();
for (key, value) in &self.variables {
env.set(key, value.clone());
}
if !self.args.is_empty() {
env.set_positional_params(self.args.clone());
}
env.set_exit_status(self.last_exit_code);
}
fn get_env_var(&self, name: &str) -> Option<String> {
self.variables.get(name).cloned()
}
fn set_env_var(&mut self, name: &str, value: String) {
self.variables.insert(name.to_string(), value);
}
fn export_var(&mut self, name: &str, value: Option<String>) {
if let Some(val) = value {
self.set_env_var(name, val.clone());
unsafe {
std::env::set_var(name, val);
}
} else if let Some(existing) = self.get_env_var(name) {
unsafe {
std::env::set_var(name, existing);
}
}
}
fn push_local_scope(&mut self) {
}
fn pop_local_scope(&mut self) {
}
fn set_positional_parameters(&mut self, params: Vec<String>) {
self.args = params;
}
fn update_exit_status(&mut self, status: i32) {
self.last_exit_code = status;
self.variables.insert("?".to_string(), status.to_string());
}
}
pub mod env_helpers {
use super::*;
pub fn initialize_shell_environment() -> Environment {
let mut env = Environment::new();
if let Some(home) = env.get("HOME") {
let rc_file = format!("{}/.flashrc", home);
if std::path::Path::new(&rc_file).exists() {
env.set("FLASH_RC_LOADED", "1".to_string());
}
}
if atty::is(atty::Stream::Stdin) {
env.shell_flags.push('i'); env.set("PS1", "flash$ ".to_string());
}
env
}
pub fn create_subshell_environment(parent: &Environment) -> Environment {
let mut subshell = parent.clone();
if let Some(shlvl) = subshell.get("SHLVL") {
if let Ok(level) = shlvl.parse::<i32>() {
subshell.set_exported("SHLVL", (level + 1).to_string());
}
}
if let Some(subshell_level) = subshell.get("FLASH_SUBSHELL") {
if let Ok(level) = subshell_level.parse::<i32>() {
subshell.set("FLASH_SUBSHELL", (level + 1).to_string());
}
} else {
subshell.set("FLASH_SUBSHELL", "1".to_string());
}
subshell
}
pub fn setup_function_environment(env: &mut Environment, func_name: &str, args: Vec<String>) {
env.push_scope();
env.set("FUNCNAME", func_name.to_string());
env.set_positional_params(args);
}
pub fn cleanup_function_environment(env: &mut Environment) {
env.pop_scope();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_environment_creation() {
let env = Environment::new();
assert!(env.has("SHELL"));
assert!(env.has("FLASH_VERSION"));
assert!(env.has("PWD"));
assert_eq!(env.get("SHELL"), Some("flash".to_string()));
}
#[test]
fn test_variable_scoping() {
let mut env = Environment::new();
env.set("TEST_VAR", "global".to_string());
assert_eq!(env.get("TEST_VAR"), Some("global".to_string()));
env.push_scope();
env.set("TEST_VAR", "local".to_string());
assert_eq!(env.get("TEST_VAR"), Some("local".to_string()));
env.pop_scope();
assert_eq!(env.get("TEST_VAR"), Some("global".to_string()));
}
#[test]
fn test_special_parameters() {
let env = Environment::new();
assert!(env.get("?").is_some());
assert!(env.get("$").is_some());
assert!(env.get("#").is_some());
}
#[test]
fn test_positional_parameters() {
let mut env = Environment::new();
env.set_positional_params(vec![
"flash".to_string(),
"arg1".to_string(),
"arg2".to_string(),
]);
assert_eq!(env.get("0"), Some("flash".to_string()));
assert_eq!(env.get("1"), Some("arg1".to_string()));
assert_eq!(env.get("2"), Some("arg2".to_string()));
assert_eq!(env.get("#"), Some("2".to_string()));
}
#[test]
fn test_export_functionality() {
let mut env = Environment::new();
env.set("TEST_EXPORT", "value".to_string());
env.export("TEST_EXPORT");
assert_eq!(std::env::var("TEST_EXPORT").ok(), Some("value".to_string()));
unsafe {
std::env::remove_var("TEST_EXPORT");
}
}
}