use std::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum ConcurrencyMode {
ReadOnly,
#[default]
Concurrent,
Exclusive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum Destructiveness {
#[default]
None,
Reversible,
Irreversible,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum InterruptBehavior {
#[default]
Cancel,
WaitComplete,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum CapabilityFlag {
Read,
Write,
Execute,
Network,
Spawn,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ToolMetadata {
pub concurrency: ConcurrencyMode,
pub destructiveness: Destructiveness,
pub interrupt: InterruptBehavior,
pub timeout: Option<Duration>,
pub capabilities: Vec<CapabilityFlag>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_concurrency: Option<usize>,
}
impl Default for ToolMetadata {
fn default() -> Self {
Self {
concurrency: ConcurrencyMode::Concurrent,
destructiveness: Destructiveness::None,
interrupt: InterruptBehavior::Cancel,
timeout: None,
capabilities: Vec::new(),
max_concurrency: None,
}
}
}
impl ToolMetadata {
#[must_use]
pub fn read_only() -> Self {
Self {
concurrency: ConcurrencyMode::ReadOnly,
capabilities: vec![CapabilityFlag::Read],
..Self::default()
}
}
#[must_use]
pub fn exclusive_write() -> Self {
Self {
concurrency: ConcurrencyMode::Exclusive,
destructiveness: Destructiveness::Reversible,
capabilities: vec![CapabilityFlag::Write],
..Self::default()
}
}
#[must_use]
pub fn spawn() -> Self {
Self {
concurrency: ConcurrencyMode::Concurrent,
destructiveness: Destructiveness::None,
capabilities: vec![CapabilityFlag::Spawn],
..Self::default()
}
}
#[must_use]
pub fn shell_execute(timeout: Duration) -> Self {
Self {
concurrency: ConcurrencyMode::Exclusive,
destructiveness: Destructiveness::Reversible,
interrupt: InterruptBehavior::Cancel,
timeout: Some(timeout),
capabilities: vec![CapabilityFlag::Execute, CapabilityFlag::Write],
max_concurrency: Some(1),
}
}
#[must_use]
pub const fn with_max_concurrency(mut self, n: Option<usize>) -> Self {
self.max_concurrency = n;
self
}
#[must_use]
pub fn allowed_in_read_only(&self) -> bool {
!self.capabilities.iter().any(|c| {
matches!(
c,
CapabilityFlag::Write | CapabilityFlag::Execute | CapabilityFlag::Spawn
)
}) && self.destructiveness == Destructiveness::None
&& self.concurrency != ConcurrencyMode::Exclusive
}
}