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};
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::*;
#[tokio::test]
async fn test_env_operations() {
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() {
let result = os().await.unwrap();
assert!(!result.is_empty());
let result = arch().await.unwrap();
assert!(!result.is_empty());
}
}