openscript_sdk 0.1.0

Standard library and AI integration for OpenScript
Documentation
//! System operations for OpenScript

use openscript::{Value, Error, Result};
use tokio::process::Command;
use std::process::Stdio;
use std::env;
use tokio::time::{sleep, Duration};
use std::time::{SystemTime, UNIX_EPOCH};

// TODO: Implement proper command registration once the openscript runtime supports it
// /// Register system commands
// pub fn register_commands(interpreter: &mut Interpreter) -> Result<()> {
//     // Process execution
//     interpreter.define_global("exec".to_string(), Value::native_function(
//         "exec", Some(1), exec_wrapper
//     ));

//     interpreter.define_global("spawn".to_string(), Value::native_function(
//         "spawn", Some(1), spawn_wrapper
//     ));

//     // Environment variables
//     interpreter.define_global("env_get".to_string(), Value::native_function(
//         "env_get", Some(1), env_get_wrapper
//     ));

//     interpreter.define_global("env_set".to_string(), Value::native_function(
//         "env_set", Some(2), env_set_wrapper
//     ));

//     interpreter.define_global("env_all".to_string(), Value::native_function(
//         "env_all", Some(0), env_all_wrapper
//     ));

//     // System information
//     interpreter.define_global("os".to_string(), Value::native_function(
//         "os", Some(0), os_wrapper
//     ));

//     interpreter.define_global("arch".to_string(), Value::native_function(
//         "arch", Some(0), arch_wrapper
//     ));

//     interpreter.define_global("hostname".to_string(), Value::native_function(
//         "hostname", Some(0), hostname_wrapper
//     ));

//     // Time functions
//     interpreter.define_global("sleep".to_string(), Value::native_function(
//         "sleep", Some(1), sleep_wrapper
//     ));

//     interpreter.define_global("time".to_string(), Value::native_function(
//         "time", Some(0), time_wrapper
//     ));

//     interpreter.define_global("timestamp".to_string(), Value::native_function(
//         "timestamp", Some(0), timestamp_wrapper
//     ));

//     Ok(())
// }

pub async fn exec(command: &str, args: &[String]) -> Result<Value> {
    let output = Command::new(command)
        .args(args)
        .output()
        .await
        .map_err(|e| Error::runtime_error(format!("Failed to execute command: {}", e)))?;

    let mut result = std::collections::HashMap::new();
    result.insert("stdout".to_string(), Value::String(String::from_utf8_lossy(&output.stdout).to_string()));
    result.insert("stderr".to_string(), Value::String(String::from_utf8_lossy(&output.stderr).to_string()));
    result.insert("status".to_string(), Value::Number(output.status.code().unwrap_or(-1) as f64));

    Ok(Value::Object(result.into_iter().collect()))
}

pub async fn spawn(command: &str, args: &[String]) -> Result<u32> {
    let child = Command::new(command)
        .args(args)
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .map_err(|e| Error::runtime_error(format!("Failed to spawn process: {}", e)))?;

    Ok(child.id().ok_or_else(|| Error::runtime_error("Failed to get process ID".to_string()))?)
}

pub async fn env_get(key: &str) -> Result<Value> {
    match env::var(key) {
        Ok(value) => Ok(Value::String(value)),
        Err(_) => Ok(Value::Null),
    }
}

pub async fn env_set(key: &str, value: &str) -> Result<()> {
    env::set_var(key, value);
    Ok(())
}

pub async fn env_all() -> Result<Value> {
    let mut env_vars = std::collections::HashMap::new();
    for (key, value) in env::vars() {
        env_vars.insert(key, Value::String(value));
    }
    Ok(Value::Object(env_vars.into_iter().collect()))
}

pub async fn os() -> Result<String> {
    Ok(env::consts::OS.to_string())
}

pub async fn arch() -> Result<String> {
    Ok(env::consts::ARCH.to_string())
}

pub async fn hostname() -> Result<String> {
    #[cfg(unix)]
    {
        use std::ffi::CStr;
        let mut buf = [0u8; 256];
        let hostname = unsafe {
            if libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, buf.len()) == 0 {
                CStr::from_ptr(buf.as_ptr() as *const libc::c_char)
                    .to_string_lossy()
                    .to_string()
            } else {
                "unknown".to_string()
            }
        };
        Ok(hostname)
    }
    #[cfg(not(unix))]
    {
        Ok("unknown".to_string())
    }
}

pub async fn sleep_ms(ms: u64) -> Result<()> {
    sleep(Duration::from_millis(ms)).await;
    Ok(())
}

pub async fn time() -> Result<u64> {
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map_err(|e| Error::runtime_error(format!("Failed to get system time: {}", e)))?;
    Ok(now.as_secs())
}

pub async fn timestamp() -> Result<u128> {
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map_err(|e| Error::runtime_error(format!("Failed to get system time: {}", e)))?;
    Ok(now.as_millis())
}

#[cfg(test)]
mod tests {
    use super::*;

    // TODO: Re-enable once command registration is implemented
    // #[test]
    // fn test_register_commands() {
    //     let mut interpreter = Interpreter::new();
    //     assert!(register_commands(&mut interpreter).is_ok());
    //     
    //     // Check that commands are registered
    //     assert!(interpreter.get_global("exec").is_some());
    //     assert!(interpreter.get_global("spawn").is_some());
    //     assert!(interpreter.get_global("env_get").is_some());
    //     assert!(interpreter.get_global("env_set").is_some());
    //     assert!(interpreter.get_global("os").is_some());
    //     assert!(interpreter.get_global("arch").is_some());
    //     assert!(interpreter.get_global("sleep").is_some());
    //     assert!(interpreter.get_global("time").is_some());
    // }

    #[tokio::test]
    async fn test_env_operations() {
        // Test env_set and env_get
        env_set("TEST_VAR", "test_value").await.unwrap();
        let result = env_get("TEST_VAR").await.unwrap();
        assert_eq!(result, Value::String("test_value".to_string()));
    }

    #[tokio::test]
    async fn test_system_info() {
        // Test os
        let result = os().await.unwrap();
        assert!(!result.is_empty());

        // Test arch
        let result = arch().await.unwrap();
        assert!(!result.is_empty());
    }
}