1#[cfg(feature = "schema")]
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6pub fn non_unique_match_preview_suffix(
7 match_previews: &[EditMatchPreview],
8 omitted_matches: usize,
9) -> String {
10 if match_previews.is_empty() {
11 return String::new();
12 }
13
14 let mut parts = match_previews
15 .iter()
16 .map(|preview| {
17 format!(
18 "line {}:{} `{}`",
19 preview.line_number, preview.column_number, preview.snippet
20 )
21 })
22 .collect::<Vec<_>>();
23
24 if omitted_matches > 0 {
25 parts.push(format!("and {omitted_matches} more"));
26 }
27
28 format!("; matches: {}", parts.join("; "))
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
32#[cfg_attr(feature = "schema", derive(JsonSchema))]
33pub struct EditMatchPreview {
34 pub line_number: usize,
35 pub column_number: usize,
36 pub snippet: String,
37}
38
39#[derive(Error, Debug, Clone, Serialize, Deserialize)]
40#[serde(tag = "code", content = "details", rename_all = "snake_case")]
41pub enum EditFailure {
42 #[error("file not found: {file_path}")]
43 FileNotFound { file_path: String },
44
45 #[error(
46 "edit #{edit_index} has an empty old_string; use write_file to create or overwrite files"
47 )]
48 EmptyOldString { edit_index: usize },
49
50 #[error("string not found for edit #{edit_index} in file {file_path}")]
51 StringNotFound {
52 file_path: String,
53 edit_index: usize,
54 },
55
56 #[error("invalid match selection for edit #{edit_index} in file {file_path}: {message}")]
57 InvalidMatchSelection {
58 file_path: String,
59 edit_index: usize,
60 message: String,
61 },
62
63 #[error(
64 "found {occurrences} matches for edit #{edit_index} in file {file_path}; old_string must match exactly once{preview_suffix}",
65 preview_suffix = non_unique_match_preview_suffix(match_previews, *omitted_matches)
66 )]
67 NonUniqueMatch {
68 file_path: String,
69 edit_index: usize,
70 occurrences: usize,
71 #[serde(default)]
72 match_previews: Vec<EditMatchPreview>,
73 #[serde(default)]
74 omitted_matches: usize,
75 },
76}
77
78#[derive(Error, Debug, Clone, Serialize, Deserialize)]
79pub enum WorkspaceError {
80 #[error("I/O error: {0}")]
81 Io(String),
82
83 #[error("Tool execution failed: {0}")]
84 ToolExecution(String),
85
86 #[error("Edit failed: {0}")]
87 Edit(EditFailure),
88
89 #[error("Transport error: {0}")]
90 Transport(String),
91
92 #[error("Status error: {0}")]
93 Status(String),
94
95 #[error("Not supported: {0}")]
96 NotSupported(String),
97
98 #[error("Invalid configuration: {0}")]
99 InvalidConfiguration(String),
100
101 #[error("Remote workspace error: {0}")]
102 Remote(String),
103}
104
105pub type Result<T> = std::result::Result<T, WorkspaceError>;
106
107#[derive(Error, Debug, Clone, Serialize, Deserialize)]
108pub enum EnvironmentManagerError {
109 #[error("Environment not found: {0}")]
110 NotFound(String),
111
112 #[error("Environment operation not supported: {0}")]
113 NotSupported(String),
114
115 #[error("Invalid environment request: {0}")]
116 InvalidRequest(String),
117
118 #[error("I/O error: {0}")]
119 Io(String),
120
121 #[error("Environment manager error: {0}")]
122 Other(String),
123}
124
125pub type EnvironmentManagerResult<T> = std::result::Result<T, EnvironmentManagerError>;
126
127impl From<std::io::Error> for EnvironmentManagerError {
128 fn from(err: std::io::Error) -> Self {
129 EnvironmentManagerError::Io(err.to_string())
130 }
131}
132
133impl From<WorkspaceError> for EnvironmentManagerError {
134 fn from(err: WorkspaceError) -> Self {
135 match err {
136 WorkspaceError::Io(message) => EnvironmentManagerError::Io(message),
137 other => EnvironmentManagerError::Other(other.to_string()),
138 }
139 }
140}
141
142#[derive(Error, Debug, Clone, Serialize, Deserialize)]
143pub enum WorkspaceManagerError {
144 #[error("Workspace not found: {0}")]
145 NotFound(String),
146
147 #[error("Workspace operation not supported: {0}")]
148 NotSupported(String),
149
150 #[error("Invalid workspace request: {0}")]
151 InvalidRequest(String),
152
153 #[error("I/O error: {0}")]
154 Io(String),
155
156 #[error("Workspace manager error: {0}")]
157 Other(String),
158}
159
160pub type WorkspaceManagerResult<T> = std::result::Result<T, WorkspaceManagerError>;
161
162impl From<std::io::Error> for WorkspaceManagerError {
163 fn from(err: std::io::Error) -> Self {
164 WorkspaceManagerError::Io(err.to_string())
165 }
166}
167
168impl From<WorkspaceError> for WorkspaceManagerError {
169 fn from(err: WorkspaceError) -> Self {
170 match err {
171 WorkspaceError::Io(message) => WorkspaceManagerError::Io(message),
172 other => WorkspaceManagerError::Other(other.to_string()),
173 }
174 }
175}
176
177impl From<tonic::transport::Error> for WorkspaceError {
178 fn from(err: tonic::transport::Error) -> Self {
179 WorkspaceError::Transport(err.to_string())
180 }
181}
182
183impl From<tonic::Status> for WorkspaceError {
184 fn from(err: tonic::Status) -> Self {
185 WorkspaceError::Status(err.to_string())
186 }
187}
188
189impl From<std::io::Error> for WorkspaceError {
190 fn from(err: std::io::Error) -> Self {
191 WorkspaceError::Io(err.to_string())
192 }
193}
194
195impl From<EditFailure> for WorkspaceError {
196 fn from(err: EditFailure) -> Self {
197 WorkspaceError::Edit(err)
198 }
199}