use std::time::Duration;
use ferrin_spec::JsonValue;
use ferrin_spec::ToolName;
type BoxError = Box<dyn std::error::Error + Send + Sync>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ToolError {
#[error("{message}")]
Message {
message: String,
#[source]
cause: Option<BoxError>,
},
#[error("tool returned error payload")]
Json {
value: JsonValue,
},
#[error("tool execution timed out after {0:?}")]
Timeout(Duration),
#[error("tool execution cancelled")]
Cancelled,
}
impl ToolError {
#[must_use]
pub fn message(message: impl Into<String>) -> Self {
Self::Message {
message: message.into(),
cause: None,
}
}
#[must_use]
pub fn json(value: JsonValue) -> Self {
Self::Json { value }
}
#[must_use]
pub fn from_error(error: impl std::error::Error + Send + Sync + 'static) -> Self {
Self::Message {
message: error.to_string(),
cause: Some(Box::new(error)),
}
}
#[must_use]
pub fn with_cause(self, cause: impl std::error::Error + Send + Sync + 'static) -> Self {
match self {
Self::Message { message, .. } => Self::Message {
message,
cause: Some(Box::new(cause)),
},
other => other,
}
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
matches!(self, Self::Cancelled)
}
}
impl From<serde_json::Error> for ToolError {
fn from(error: serde_json::Error) -> Self {
Self::from_error(error)
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("tool `{name}` is already defined")]
pub struct DuplicateToolError {
pub name: ToolName,
}