coderlib 0.1.0

A Rust library for AI-powered code assistance and agentic system
Documentation
//! File operation tools for CoderLib
//!
//! This module provides tools for reading, writing, and manipulating files.

use async_trait::async_trait;
use std::path::PathBuf;

use crate::integration::HostIntegration;
use crate::tools::{Tool, ToolResponse, ToolError, Permission, validation};

/// Tool for reading file contents
pub struct FileReadTool;

impl FileReadTool {
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl Tool for FileReadTool {
    async fn execute(
        &self,
        parameters: serde_json::Value,
        host: &dyn HostIntegration,
    ) -> Result<ToolResponse, ToolError> {
        let path = validation::require_path(&parameters, "path")?;
        validation::validate_safe_path(&path)?;
        
        let content = host.get_file_content(&path).await
            .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
        
        Ok(ToolResponse::success(content))
    }
    
    fn requires_permission(&self) -> Permission {
        Permission::ReadFile(PathBuf::new())
    }
    
    fn description(&self) -> &str {
        "Read the contents of a file"
    }
    
    fn name(&self) -> &str {
        "file_read"
    }
    
    fn parameter_schema(&self) -> serde_json::Value {
        serde_json::json!({
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Path to the file to read"
                }
            },
            "required": ["path"]
        })
    }
    
    fn clone_box(&self) -> Box<dyn Tool> {
        Box::new(FileReadTool::new())
    }
}

/// Tool for writing file contents
pub struct FileWriteTool;

impl FileWriteTool {
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl Tool for FileWriteTool {
    async fn execute(
        &self,
        parameters: serde_json::Value,
        host: &dyn HostIntegration,
    ) -> Result<ToolResponse, ToolError> {
        let path = validation::require_path(&parameters, "path")?;
        let content = validation::require_string(&parameters, "content")?;
        validation::validate_safe_path(&path)?;
        
        host.update_file_content(&path, &content).await
            .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
        
        Ok(ToolResponse::with_files(
            format!("Successfully wrote {} bytes to {}", content.len(), path.display()),
            vec![path]
        ))
    }
    
    fn requires_permission(&self) -> Permission {
        Permission::WriteFile(PathBuf::new())
    }
    
    fn description(&self) -> &str {
        "Write content to a file"
    }
    
    fn name(&self) -> &str {
        "file_write"
    }
    
    fn parameter_schema(&self) -> serde_json::Value {
        serde_json::json!({
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Path to the file to write"
                },
                "content": {
                    "type": "string",
                    "description": "Content to write to the file"
                }
            },
            "required": ["path", "content"]
        })
    }
    
    fn clone_box(&self) -> Box<dyn Tool> {
        Box::new(FileWriteTool::new())
    }
}

/// Tool for listing files in a directory
pub struct FileListTool;

impl FileListTool {
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl Tool for FileListTool {
    async fn execute(
        &self,
        parameters: serde_json::Value,
        _host: &dyn HostIntegration,
    ) -> Result<ToolResponse, ToolError> {
        let path = validation::require_path(&parameters, "path")?;
        validation::validate_safe_path(&path)?;
        
        let entries = std::fs::read_dir(&path)
            .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
        
        let mut files = Vec::new();
        let mut dirs = Vec::new();
        
        for entry in entries {
            let entry = entry.map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
            let path = entry.path();
            let name = path.file_name()
                .and_then(|n| n.to_str())
                .unwrap_or("?")
                .to_string();
            
            if path.is_dir() {
                dirs.push(name);
            } else {
                files.push(name);
            }
        }
        
        dirs.sort();
        files.sort();
        
        let mut result = String::new();
        if !dirs.is_empty() {
            result.push_str("Directories:\n");
            for dir in dirs {
                result.push_str(&format!("  {}/\n", dir));
            }
        }
        
        if !files.is_empty() {
            if !result.is_empty() {
                result.push('\n');
            }
            result.push_str("Files:\n");
            for file in files {
                result.push_str(&format!("  {}\n", file));
            }
        }
        
        if result.is_empty() {
            result = "Directory is empty".to_string();
        }
        
        Ok(ToolResponse::success(result))
    }
    
    fn requires_permission(&self) -> Permission {
        Permission::ReadFile(PathBuf::new())
    }
    
    fn description(&self) -> &str {
        "List files and directories in a directory"
    }
    
    fn name(&self) -> &str {
        "file_list"
    }
    
    fn parameter_schema(&self) -> serde_json::Value {
        serde_json::json!({
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Path to the directory to list"
                }
            },
            "required": ["path"]
        })
    }
    
    fn clone_box(&self) -> Box<dyn Tool> {
        Box::new(FileListTool::new())
    }
}