use rmcp::schemars::{self, JsonSchema};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ReadFileArgs {
#[schemars(description = "The path to the file to read")]
pub path: String,
#[schemars(description = "Number of lines to read from the beginning")]
#[serde(default)]
pub head: Option<usize>,
#[schemars(description = "Number of lines to read from the end")]
#[serde(default)]
pub tail: Option<usize>,
}
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct WriteFileArgs {
#[schemars(description = "The path to write the file to")]
pub path: String,
#[schemars(description = "The content to write to the file")]
pub content: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct ListDirectoryArgs {
#[schemars(description = "The path to the directory to list")]
pub path: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CreateDirectoryArgs {
#[schemars(description = "The path of the directory to create")]
pub path: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GetFileInfoArgs {
#[schemars(description = "The path to get information for")]
pub path: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct MoveFileArgs {
#[schemars(description = "The source file path")]
pub source: String,
#[schemars(description = "The destination file path")]
pub destination: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SearchFilesArgs {
#[schemars(description = "Directory path to search in")]
pub path: String,
#[schemars(description = "Glob pattern to match file names (e.g., *.txt)")]
pub pattern: String,
#[schemars(description = "Patterns to exclude from results")]
#[serde(default)]
pub exclude_patterns: Vec<String>,
}
#[derive(Debug, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct FileInfo {
pub name: String,
pub path: String,
pub file_type: String,
pub size: u64,
pub created: Option<String>,
pub modified: Option<String>,
pub accessed: Option<String>,
pub permissions: Option<String>,
}
#[derive(Debug, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct DirectoryInfo {
pub name: String,
pub path: String,
pub children: Vec<String>,
}
#[derive(Debug, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct SearchResult {
pub path: String,
pub file_type: String,
pub size: u64,
}
#[allow(dead_code)]
pub fn format_size(size: u64) -> String {
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
if size >= GB {
format!("{:.2} GB", size as f64 / GB as f64)
} else if size >= MB {
format!("{:.2} MB", size as f64 / MB as f64)
} else if size >= KB {
format!("{:.2} KB", size as f64 / KB as f64)
} else {
format!("{} bytes", size)
}
}
pub fn format_time(time: cap_std::time::SystemTime) -> String {
let std_time = time.into_std();
use std::time::UNIX_EPOCH;
let duration = std_time.duration_since(UNIX_EPOCH).unwrap_or_default();
let secs = duration.as_secs();
let datetime: chrono::DateTime<chrono::Utc> =
chrono::DateTime::from_timestamp(secs as i64, 0).unwrap_or_else(chrono::Utc::now);
datetime.format("%Y-%m-%d %H:%M:%S UTC").to_string()
}
pub fn glob_match(pattern: &str, name: &str) -> bool {
if pattern.contains('*') {
glob::Pattern::new(pattern)
.map(|p| p.matches(name))
.unwrap_or(false)
} else {
name.contains(pattern)
}
}