mod context;
mod edit;
mod glob;
mod grep;
mod read;
mod rig_adapter;
mod submit_tool;
mod write;
pub use context::{PermissionMode, ToolContext, ToolEvent, ToolOperation};
pub use edit::{EditParams, EditResult, EditTool};
pub use glob::{GlobParams, GlobResult, GlobTool};
pub use grep::{GrepOutputMode, GrepParams, GrepResult, GrepTool};
pub use read::{ReadParams, ReadResult, ReadTool};
pub use rig_adapter::{create_rig_file_tools, RigFileTool};
pub use submit_tool::{DynamicSubmitTool, ToolDefinition};
pub use write::{WriteParams, WriteResult, WriteTool};
use crate::error::NikaError;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[async_trait]
pub trait FileTool: Send + Sync {
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn parameters_schema(&self) -> Value;
async fn call(&self, params: Value) -> Result<ToolOutput, NikaError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolOutput {
pub content: String,
pub is_error: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
impl ToolOutput {
pub fn success(content: impl Into<String>) -> Self {
Self {
content: content.into(),
is_error: false,
data: None,
}
}
pub fn success_with_data(content: impl Into<String>, data: Value) -> Self {
Self {
content: content.into(),
is_error: false,
data: Some(data),
}
}
pub fn error(content: impl Into<String>) -> Self {
Self {
content: content.into(),
is_error: true,
data: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolErrorCode {
ReadFailed = 200,
WriteFailed = 201,
EditFailed = 202,
MustReadFirst = 203,
PathOutOfBounds = 204,
PermissionDenied = 205,
InvalidGlobPattern = 206,
InvalidRegex = 207,
FileNotFound = 208,
OldStringNotUnique = 209,
FileAlreadyExists = 210,
RelativePath = 211,
}
impl ToolErrorCode {
pub fn code(&self) -> String {
format!("NIKA-{}", *self as u16)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tool_output_success() {
let output = ToolOutput::success("File read successfully");
assert!(!output.is_error);
assert_eq!(output.content, "File read successfully");
assert!(output.data.is_none());
}
#[test]
fn test_tool_output_error() {
let output = ToolOutput::error("File not found");
assert!(output.is_error);
assert_eq!(output.content, "File not found");
}
#[test]
fn test_tool_error_codes() {
assert_eq!(ToolErrorCode::ReadFailed.code(), "NIKA-200");
assert_eq!(ToolErrorCode::MustReadFirst.code(), "NIKA-203");
assert_eq!(ToolErrorCode::PathOutOfBounds.code(), "NIKA-204");
}
}