statespace_tool_runtime/
error.rs1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, Error>;
6
7#[derive(Debug, Error)]
8#[non_exhaustive]
9pub enum Error {
10 #[error("invalid command: {0}")]
11 InvalidCommand(String),
12
13 #[error("command not found in frontmatter: {command}")]
14 CommandNotFound { command: String },
15
16 #[error("no frontmatter found in file")]
17 NoFrontmatter,
18
19 #[error("frontmatter parse error: {0}")]
20 FrontmatterParse(String),
21
22 #[error("tool execution timeout")]
23 Timeout,
24
25 #[error("output too large: {size} bytes (limit: {limit})")]
26 OutputTooLarge { size: usize, limit: usize },
27
28 #[error("path traversal attempt: tried to access {attempted} outside boundary {boundary}")]
29 PathTraversal { attempted: String, boundary: String },
30
31 #[error("file not found: {0}")]
32 NotFound(String),
33
34 #[error("security violation: {0}")]
35 Security(String),
36
37 #[error("network error: {0}")]
38 Network(String),
39
40 #[error("io error: {0}")]
41 Io(#[from] std::io::Error),
42
43 #[error("internal error: {0}")]
44 Internal(String),
45}
46
47impl Error {
48 #[must_use]
49 pub fn user_message(&self) -> String {
50 match self {
51 Self::InvalidCommand(msg) => format!("Invalid command: {msg}"),
52 Self::CommandNotFound { command } => {
53 format!("Command '{command}' not allowed by frontmatter")
54 }
55 Self::NoFrontmatter => {
56 "No frontmatter found. Tools must be declared in YAML/TOML frontmatter.".to_string()
57 }
58 Self::FrontmatterParse(msg) => format!("Frontmatter parse error: {msg}"),
59 Self::Timeout => "Tool execution timeout".to_string(),
60 Self::OutputTooLarge { size, limit } => {
61 format!("Output too large: {size} bytes (limit: {limit} bytes)")
62 }
63 Self::PathTraversal { attempted, .. } => {
64 format!("Access denied: cannot access '{attempted}'")
65 }
66 Self::NotFound(path) => format!("File not found: {path}"),
67 Self::Security(msg) => format!("Security violation: {msg}"),
68 Self::Network(msg) => format!("Network error: {msg}"),
69 Self::Io(e) => format!("IO error: {e}"),
70 Self::Internal(_) => "Internal server error".to_string(),
71 }
72 }
73
74 #[must_use]
76 pub const fn http_status_code(&self) -> u16 {
77 match self {
78 Self::InvalidCommand(_)
79 | Self::CommandNotFound { .. }
80 | Self::NoFrontmatter
81 | Self::FrontmatterParse(_) => 400,
82
83 Self::PathTraversal { .. } | Self::Security(_) => 403,
84
85 Self::NotFound(_) => 404,
86 Self::Timeout => 504,
87 Self::OutputTooLarge { .. } => 413,
88
89 Self::Io(_) | Self::Internal(_) => 500,
90 Self::Network(_) => 502,
91 }
92 }
93}