use crate::models::ToolDefinition;
use crate::parser::ToolParser;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct Tool {
pub name: String,
pub path: PathBuf,
pub language: ToolLanguage,
pub definition: ToolDefinition,
pub declaration: crate::core::function::FunctionDeclaration,
}
impl Tool {
pub async fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref().to_path_buf();
let name = path
.file_stem()
.ok_or_else(|| anyhow::anyhow!("Invalid tool filename: {}", path.display()))?
.to_string_lossy()
.to_string();
let language = ToolLanguage::from_extension(
path.extension().and_then(|ext| ext.to_str()).unwrap_or(""),
)?;
let content = tokio::fs::read_to_string(&path).await?;
let parser = ToolParser::new(language);
let definition = parser.parse(&content)?;
let declaration = definition.to_function_declaration(&name)?;
Ok(Self {
name,
path,
language,
definition,
declaration,
})
}
pub fn get_command(&self) -> Vec<String> {
match self.language {
ToolLanguage::Bash => vec!["bash".to_string(), self.path.to_string_lossy().to_string()],
ToolLanguage::JavaScript => {
vec!["node".to_string(), self.path.to_string_lossy().to_string()]
}
ToolLanguage::Python => vec![
"python".to_string(),
self.path.to_string_lossy().to_string(),
],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ToolLanguage {
Bash,
JavaScript,
Python,
}
impl ToolLanguage {
pub fn from_extension(ext: &str) -> Result<Self> {
match ext {
"sh" => Ok(Self::Bash),
"js" => Ok(Self::JavaScript),
"py" => Ok(Self::Python),
_ => anyhow::bail!("Unsupported file extension for a tool: {}", ext),
}
}
pub fn extension(&self) -> &'static str {
match self {
Self::Bash => "sh",
Self::JavaScript => "js",
Self::Python => "py",
}
}
}
impl std::fmt::Display for ToolLanguage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Bash => "bash",
Self::JavaScript => "javascript",
Self::Python => "python",
})
}
}