1use serde_json::Value as JsonValue;
7use thiserror::Error;
8
9use crate::output::OutputData;
10use crate::result::{value_to_json, ExecResult};
11use crate::tool::ToolSchema;
12
13pub type BackendResult<T> = Result<T, BackendError>;
15
16#[derive(Debug, Clone, Error)]
18pub enum BackendError {
19 #[error("not found: {0}")]
20 NotFound(String),
21 #[error("already exists: {0}")]
22 AlreadyExists(String),
23 #[error("permission denied: {0}")]
24 PermissionDenied(String),
25 #[error("is a directory: {0}")]
26 IsDirectory(String),
27 #[error("not a directory: {0}")]
28 NotDirectory(String),
29 #[error("read-only filesystem")]
30 ReadOnly,
31 #[error("conflict: {0}")]
32 Conflict(ConflictError),
33 #[error("tool not found: {0}")]
34 ToolNotFound(String),
35 #[error("io error: {0}")]
36 Io(String),
37 #[error("invalid operation: {0}")]
38 InvalidOperation(String),
39}
40
41impl From<std::io::Error> for BackendError {
42 fn from(err: std::io::Error) -> Self {
43 use std::io::ErrorKind;
44 match err.kind() {
45 ErrorKind::NotFound => BackendError::NotFound(err.to_string()),
46 ErrorKind::AlreadyExists => BackendError::AlreadyExists(err.to_string()),
47 ErrorKind::PermissionDenied => BackendError::PermissionDenied(err.to_string()),
48 ErrorKind::IsADirectory => BackendError::IsDirectory(err.to_string()),
49 ErrorKind::NotADirectory => BackendError::NotDirectory(err.to_string()),
50 ErrorKind::ReadOnlyFilesystem => BackendError::ReadOnly,
51 _ => BackendError::Io(err.to_string()),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Error)]
58#[error("conflict at {location}: expected {expected:?}, found {actual:?}")]
59pub struct ConflictError {
60 pub location: String,
62 pub expected: String,
64 pub actual: String,
66}
67
68#[derive(Debug, Clone)]
83pub enum PatchOp {
84 Insert { offset: usize, content: String },
86
87 Delete {
90 offset: usize,
91 len: usize,
92 expected: Option<String>,
93 },
94
95 Replace {
98 offset: usize,
99 len: usize,
100 content: String,
101 expected: Option<String>,
102 },
103
104 InsertLine { line: usize, content: String },
106
107 DeleteLine { line: usize, expected: Option<String> },
110
111 ReplaceLine {
114 line: usize,
115 content: String,
116 expected: Option<String>,
117 },
118
119 Append { content: String },
121}
122
123#[derive(Debug, Clone, Default)]
125pub struct ReadRange {
126 pub start_line: Option<usize>,
128 pub end_line: Option<usize>,
130 pub offset: Option<u64>,
132 pub limit: Option<u64>,
134}
135
136impl ReadRange {
137 pub fn lines(start: usize, end: usize) -> Self {
139 Self {
140 start_line: Some(start),
141 end_line: Some(end),
142 ..Default::default()
143 }
144 }
145
146 pub fn bytes(offset: u64, limit: u64) -> Self {
148 Self {
149 offset: Some(offset),
150 limit: Some(limit),
151 ..Default::default()
152 }
153 }
154}
155
156#[non_exhaustive]
158#[derive(Debug, Clone, Copy, Default)]
159pub enum WriteMode {
160 CreateNew,
162 #[default]
164 Overwrite,
165 UpdateOnly,
167 Truncate,
169}
170
171#[derive(Debug, Clone)]
173pub struct ToolResult {
174 pub code: i32,
176 pub stdout: String,
178 pub stderr: String,
180 pub data: Option<JsonValue>,
182 pub output: Option<OutputData>,
184}
185
186impl ToolResult {
187 pub fn success(stdout: impl Into<String>) -> Self {
189 Self {
190 code: 0,
191 stdout: stdout.into(),
192 stderr: String::new(),
193 data: None,
194 output: None,
195 }
196 }
197
198 pub fn failure(code: i32, stderr: impl Into<String>) -> Self {
200 Self {
201 code,
202 stdout: String::new(),
203 stderr: stderr.into(),
204 data: None,
205 output: None,
206 }
207 }
208
209 pub fn with_data(stdout: impl Into<String>, data: JsonValue) -> Self {
211 Self {
212 code: 0,
213 stdout: stdout.into(),
214 stderr: String::new(),
215 data: Some(data),
216 output: None,
217 }
218 }
219
220 pub fn ok(&self) -> bool {
222 self.code == 0
223 }
224}
225
226impl From<ExecResult> for ToolResult {
227 fn from(exec: ExecResult) -> Self {
228 let code = exec.code.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
230
231 let data = exec.data.map(|v| value_to_json(&v));
233
234 Self {
235 code,
236 stdout: exec.out,
237 stderr: exec.err,
238 data,
239 output: exec.output,
240 }
241 }
242}
243
244#[derive(Debug, Clone)]
246pub struct ToolInfo {
247 pub name: String,
249 pub description: String,
251 pub schema: ToolSchema,
253}