use anyhow::{Context, Result};
use std::fmt::Display;
use std::path::Path;
pub const PATCH_PARAMETER_HINT: &str = "(use 'input' or 'patch' parameter)";
pub const USE_PATCH_FOR_LARGE_EDITS: &str = "For larger edits, use apply_patch instead.";
pub const USE_PATCH_OR_WRITE_FOR_LARGE_FILES: &str = "For large files, use apply_patch or a dedicated write operation.";
pub const READ_FILE_FIRST_HINT: &str = "Use read_file first to get the exact text, then copy it precisely.";
pub fn with_file_context<T, E>(result: std::result::Result<T, E>, operation: impl Display, path: &Path) -> Result<T>
where
E: std::error::Error + Send + Sync + 'static,
{
result.with_context(|| format!("Failed to {operation} '{}'", path.display()))
}
pub fn with_path_context<T, E>(
result: std::result::Result<T, E>,
operation: impl Display,
path: impl Display,
) -> Result<T>
where
E: std::error::Error + Send + Sync + 'static,
{
result.with_context(|| format!("Failed to {operation} {path}"))
}
#[cold]
pub fn require_string_field(args: &serde_json::Value, field: &str, tool_name: &str) -> Result<String> {
args.get(field)
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.ok_or_else(|| anyhow::anyhow!("{field} is required for {tool_name}"))
}
pub fn optional_string_field(args: &serde_json::Value, field: &str) -> Option<String> {
args.get(field).and_then(|v| v.as_str()).map(|s| s.to_string())
}
#[cold]
pub fn require_int_field(args: &serde_json::Value, field: &str, tool_name: &str) -> Result<i64> {
args.get(field)
.and_then(|v| v.as_i64())
.ok_or_else(|| anyhow::anyhow!("{field} is required for {tool_name}"))
}
#[cold]
pub fn require_field<T>(value: Option<T>, field: &str, tool_name: &str) -> Result<T> {
value.ok_or_else(|| anyhow::anyhow!("{field} is required for {tool_name}"))
}
pub fn deserialize_tool_args<T: serde::de::DeserializeOwned>(args: &serde_json::Value, tool_name: &str) -> Result<T> {
serde_json::from_value(args.clone()).map_err(|e| anyhow::anyhow!("Error: Invalid '{tool_name}' arguments: {e}"))
}