Skip to main content

vtcode_core/tools/
error_helpers.rs

1//! Shared error helpers to reduce repetitive .with_context() patterns
2
3use anyhow::{Context, Result};
4use std::fmt::Display;
5use std::path::Path;
6
7/// Wrap a file operation result with standardized path context.
8/// Works with any `Result<T, E>` where `E` implements `std::error::Error`.
9pub fn with_file_context<T, E>(
10    result: std::result::Result<T, E>,
11    operation: impl Display,
12    path: &Path,
13) -> Result<T>
14where
15    E: std::error::Error + Send + Sync + 'static,
16{
17    result.with_context(|| format!("Failed to {operation} '{}'", path.display()))
18}
19
20/// Like [`with_file_context`] but accepts a string path instead of `&Path`.
21pub fn with_path_context<T, E>(
22    result: std::result::Result<T, E>,
23    operation: impl Display,
24    path: impl Display,
25) -> Result<T>
26where
27    E: std::error::Error + Send + Sync + 'static,
28{
29    result.with_context(|| format!("Failed to {operation} {path}"))
30}
31
32/// Extract a required string field from JSON args, with a consistent error message
33#[cold]
34pub fn require_string_field(
35    args: &serde_json::Value,
36    field: &str,
37    tool_name: &str,
38) -> Result<String> {
39    args.get(field)
40        .and_then(|v| v.as_str())
41        .map(|s| s.to_string())
42        .ok_or_else(|| anyhow::anyhow!("{field} is required for {tool_name}"))
43}
44
45/// Extract an optional string field from JSON args
46pub fn optional_string_field(args: &serde_json::Value, field: &str) -> Option<String> {
47    args.get(field)
48        .and_then(|v| v.as_str())
49        .map(|s| s.to_string())
50}
51
52/// Extract a required integer field from JSON args
53#[cold]
54pub fn require_int_field(args: &serde_json::Value, field: &str, tool_name: &str) -> Result<i64> {
55    args.get(field)
56        .and_then(|v| v.as_i64())
57        .ok_or_else(|| anyhow::anyhow!("{field} is required for {tool_name}"))
58}
59
60/// Extract a required value from an `Option<T>`, with a consistent error message
61/// referencing the field name and tool/operation name.
62#[cold]
63pub fn require_field<T>(value: Option<T>, field: &str, tool_name: &str) -> Result<T> {
64    value.ok_or_else(|| anyhow::anyhow!("{field} is required for {tool_name}"))
65}
66
67/// Deserialize JSON tool arguments into a typed struct with a consistent error message.
68///
69/// Replaces the repeated pattern of `serde_json::from_value(args).context("Error: Invalid 'X' arguments...")`
70/// scattered across tool implementations.
71pub fn deserialize_tool_args<T: serde::de::DeserializeOwned>(
72    args: &serde_json::Value,
73    tool_name: &str,
74) -> Result<T> {
75    serde_json::from_value(args.clone())
76        .map_err(|e| anyhow::anyhow!("Error: Invalid '{}' arguments: {}", tool_name, e))
77}