openscript_sdk 0.1.0

Standard library and AI integration for OpenScript
Documentation
//! File system operations for OpenScript

use openscript::{Value, Error, Result};
use tokio::fs;
use std::path::Path;
use tokio::io::AsyncWriteExt;

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

//     interpreter.define_global("write_file".to_string(), Value::native_function(
//         "write_file", Some(2), write_file_wrapper
//     ));

//     interpreter.define_global("append_file".to_string(), Value::native_function(
//         "append_file", Some(2), append_file_wrapper
//     ));

//     // File/directory operations
//     interpreter.define_global("exists".to_string(), Value::native_function(
//         "exists", Some(1), exists_wrapper
//     ));

//     interpreter.define_global("is_file".to_string(), Value::native_function(
//         "is_file", Some(1), is_file_wrapper
//     ));

//     interpreter.define_global("is_dir".to_string(), Value::native_function(
//         "is_dir", Some(1), is_dir_wrapper
//     ));

//     interpreter.define_global("mkdir".to_string(), Value::native_function(
//         "mkdir", Some(1), mkdir_wrapper
//     ));

//     interpreter.define_global("rmdir".to_string(), Value::native_function(
//         "rmdir", Some(1), rmdir_wrapper
//     ));

//     interpreter.define_global("remove".to_string(), Value::native_function(
//         "remove", Some(1), remove_wrapper
//     ));

//     interpreter.define_global("copy".to_string(), Value::native_function(
//         "copy", Some(2), copy_wrapper
//     ));

//     interpreter.define_global("move".to_string(), Value::native_function(
//         "move", Some(2), move_wrapper
//     ));

//     interpreter.define_global("list_dir".to_string(), Value::native_function(
//         "list_dir", Some(1), list_dir_wrapper
//     ));

//     interpreter.define_global("file_size".to_string(), Value::native_function(
//         "file_size", Some(1), file_size_wrapper
//     ));

//     Ok(())
// }

pub async fn read_file(path: &str) -> Result<String> {
    match fs::read_to_string(path).await {
        Ok(content) => Ok(content),
        Err(e) => Err(Error::runtime_error(format!("Failed to read file '{}': {}", path, e))),
    }
}

pub async fn write_file(path: &str, content: &str) -> Result<()> {
    match fs::write(path, content).await {
        Ok(()) => Ok(()),
        Err(e) => Err(Error::runtime_error(format!("Failed to write file '{}': {}", path, e))),
    }
}

pub async fn append_file(path: &str, content: &str) -> Result<()> {
    match fs::OpenOptions::new().create(true).append(true).open(path).await {
        Ok(mut file) => {
            match file.write_all(content.as_bytes()).await {
                Ok(()) => Ok(()),
                Err(e) => Err(Error::runtime_error(format!("Failed to append to file '{}': {}", path, e))),
            }
        }
        Err(e) => Err(Error::runtime_error(format!("Failed to open file '{}': {}", path, e))),
    }
}

pub async fn exists(path: &str) -> Result<bool> {
    Ok(Path::new(path).exists())
}

pub async fn is_file(path: &str) -> Result<bool> {
    Ok(Path::new(path).is_file())
}

pub async fn is_dir(path: &str) -> Result<bool> {
    Ok(Path::new(path).is_dir())
}

pub async fn mkdir(path: &str) -> Result<()> {
    match fs::create_dir_all(path).await {
        Ok(()) => Ok(()),
        Err(e) => Err(Error::runtime_error(format!("Failed to create directory '{}': {}", path, e))),
    }
}

pub async fn rmdir(path: &str) -> Result<()> {
    match fs::remove_dir(path).await {
        Ok(()) => Ok(()),
        Err(e) => Err(Error::runtime_error(format!("Failed to remove directory '{}': {}", path, e))),
    }
}

pub async fn remove(path: &str) -> Result<()> {
    match fs::remove_file(path).await {
        Ok(()) => Ok(()),
        Err(e) => Err(Error::runtime_error(format!("Failed to remove file '{}': {}", path, e))),
    }
}

pub async fn copy(from: &str, to: &str) -> Result<()> {
    match fs::copy(from, to).await {
        Ok(_) => Ok(()),
        Err(e) => Err(Error::runtime_error(format!("Failed to copy '{}' to '{}': {}", from, to, e))),
    }
}

pub async fn rename(from: &str, to: &str) -> Result<()> {
    match fs::rename(from, to).await {
        Ok(()) => Ok(()),
        Err(e) => Err(Error::runtime_error(format!("Failed to move '{}' to '{}': {}", from, to, e))),
    }
}

pub async fn list_dir(path: &str) -> Result<Vec<Value>> {
    match fs::read_dir(path).await {
        Ok(mut entries) => {
            let mut items = Vec::new();
            while let Ok(Some(entry)) = entries.next_entry().await {
                if let Some(name) = entry.file_name().to_str() {
                    items.push(Value::String(name.to_string()));
                }
            }
            Ok(items)
        }
        Err(e) => Err(Error::runtime_error(format!("Failed to list directory '{}': {}", path, e))),
    }
}

pub async fn file_size(path: &str) -> Result<u64> {
    match fs::metadata(path).await {
        Ok(metadata) => Ok(metadata.len()),
        Err(e) => Err(Error::runtime_error(format!("Failed to get file size for '{}': {}", path, e))),
    }
}

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

    // 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("read_file").is_some());
    //     assert!(interpreter.get_global("write_file").is_some());
    //     assert!(interpreter.get_global("exists").is_some());
    //     assert!(interpreter.get_global("is_file").is_some());
    //     assert!(interpreter.get_global("is_dir").is_some());
    //     assert!(interpreter.get_global("mkdir").is_some());
    //     assert!(interpreter.get_global("list_dir").is_some());
    // }

    #[tokio::test]
    async fn test_file_operations() {
        let temp_dir = TempDir::new().unwrap();
        let file_path = temp_dir.path().join("test.txt");
        let file_path_str = file_path.to_str().unwrap();

        // Test write_file
        write_file(file_path_str, "Hello, World!").await.unwrap();

        // Test read_file
        let result = read_file(file_path_str).await.unwrap();
        assert_eq!(result, "Hello, World!");

        // Test exists
        let result = exists(file_path_str).await.unwrap();
        assert_eq!(result, true);

        // Test is_file
        let result = is_file(file_path_str).await.unwrap();
        assert_eq!(result, true);

        // Test file_size
        let result = file_size(file_path_str).await.unwrap();
        assert_eq!(result, 13); // "Hello, World!" is 13 bytes
    }
}