1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
11pub struct ItemInfo {
12 pub id: String,
13 pub name: String,
14 pub kind: String,
15 pub path: Vec<String>,
16 pub docs: Option<String>,
17 pub visibility: String,
18}
19
20#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
22pub struct ItemPreview {
23 pub id: String,
24 pub name: String,
25 pub kind: String,
26 pub path: Vec<String>,
27}
28
29#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
31pub struct PaginationInfo {
32 pub total: usize,
33 pub limit: usize,
34 pub offset: usize,
35 pub has_more: bool,
36}
37
38#[derive(Debug, Serialize, Deserialize, PartialEq)]
40pub struct ListCrateItemsOutput {
41 pub items: Vec<ItemInfo>,
42 pub pagination: PaginationInfo,
43}
44
45impl ListCrateItemsOutput {
46 pub fn to_json(&self) -> String {
48 serde_json::to_string(self)
49 .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
50 }
51}
52
53#[derive(Debug, Serialize, Deserialize, PartialEq)]
55pub struct SearchItemsOutput {
56 pub items: Vec<ItemInfo>,
57 pub pagination: PaginationInfo,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub warning: Option<String>,
60}
61
62impl SearchItemsOutput {
63 pub fn to_json(&self) -> String {
65 serde_json::to_string(self)
66 .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
67 }
68}
69
70#[derive(Debug, Serialize, Deserialize, PartialEq)]
72pub struct SearchItemsPreviewOutput {
73 pub items: Vec<ItemPreview>,
74 pub pagination: PaginationInfo,
75}
76
77impl SearchItemsPreviewOutput {
78 pub fn to_json(&self) -> String {
80 serde_json::to_string(self)
81 .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
82 }
83}
84
85#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
87pub struct SourceLocation {
88 pub filename: String,
89 pub line_start: usize,
90 pub column_start: usize,
91 pub line_end: usize,
92 pub column_end: usize,
93}
94
95#[derive(Debug, Serialize, Deserialize, PartialEq)]
97pub struct DetailedItem {
98 pub info: ItemInfo,
99 pub signature: Option<String>,
100 pub generics: Option<serde_json::Value>,
101 pub fields: Option<Vec<ItemInfo>>,
102 pub variants: Option<Vec<ItemInfo>>,
103 pub methods: Option<Vec<ItemInfo>>,
104 pub source_location: Option<SourceLocation>,
105}
106
107#[derive(Debug, Serialize, Deserialize, PartialEq)]
109#[serde(untagged)]
110pub enum GetItemDetailsOutput {
111 Success(Box<DetailedItem>),
112 Error { error: String },
113}
114
115impl GetItemDetailsOutput {
116 pub fn to_json(&self) -> String {
118 serde_json::to_string(self)
119 .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
120 }
121
122 pub fn is_success(&self) -> bool {
124 matches!(self, GetItemDetailsOutput::Success(_))
125 }
126
127 pub fn is_error(&self) -> bool {
129 matches!(self, GetItemDetailsOutput::Error { .. })
130 }
131}
132
133#[derive(Debug, Serialize, Deserialize, PartialEq)]
135pub struct GetItemDocsOutput {
136 pub documentation: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub message: Option<String>,
139}
140
141impl GetItemDocsOutput {
142 pub fn to_json(&self) -> String {
144 serde_json::to_string(self)
145 .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
146 }
147}
148
149#[derive(Debug, Serialize, Deserialize, PartialEq)]
151pub struct SourceInfo {
152 pub location: SourceLocation,
153 pub code: String,
154 pub context_lines: Option<usize>,
155}
156
157#[derive(Debug, Serialize, Deserialize, PartialEq)]
159#[serde(untagged)]
160pub enum GetItemSourceOutput {
161 Success(SourceInfo),
162 Error { error: String },
163}
164
165impl GetItemSourceOutput {
166 pub fn to_json(&self) -> String {
168 serde_json::to_string(self)
169 .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
170 }
171
172 pub fn is_success(&self) -> bool {
174 matches!(self, GetItemSourceOutput::Success(_))
175 }
176
177 pub fn is_error(&self) -> bool {
179 matches!(self, GetItemSourceOutput::Error { .. })
180 }
181}
182
183#[derive(Debug, Serialize, Deserialize, PartialEq)]
185pub struct DocsErrorOutput {
186 pub error: String,
187}
188
189impl DocsErrorOutput {
190 pub fn new(message: impl Into<String>) -> Self {
192 Self {
193 error: message.into(),
194 }
195 }
196
197 pub fn to_json(&self) -> String {
199 serde_json::to_string(self)
200 .unwrap_or_else(|_| r#"{"error":"Failed to serialize error"}"#.to_string())
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn test_list_items_output_serialization() {
210 let output = ListCrateItemsOutput {
211 items: vec![ItemInfo {
212 id: "1".to_string(),
213 name: "test_fn".to_string(),
214 kind: "function".to_string(),
215 path: vec!["test".to_string()],
216 docs: Some("Test function".to_string()),
217 visibility: "public".to_string(),
218 }],
219 pagination: PaginationInfo {
220 total: 1,
221 limit: 100,
222 offset: 0,
223 has_more: false,
224 },
225 };
226
227 let json = output.to_json();
228 let deserialized: ListCrateItemsOutput = serde_json::from_str(&json).unwrap();
229 assert_eq!(output, deserialized);
230 }
231
232 #[test]
233 fn test_search_preview_output() {
234 let output = SearchItemsPreviewOutput {
235 items: vec![ItemPreview {
236 id: "42".to_string(),
237 name: "MyStruct".to_string(),
238 kind: "struct".to_string(),
239 path: vec!["my_mod".to_string()],
240 }],
241 pagination: PaginationInfo {
242 total: 1,
243 limit: 10,
244 offset: 0,
245 has_more: false,
246 },
247 };
248
249 let json = output.to_json();
250 let deserialized: SearchItemsPreviewOutput = serde_json::from_str(&json).unwrap();
251 assert_eq!(output, deserialized);
252 }
253
254 #[test]
255 fn test_item_details_output() {
256 let success = GetItemDetailsOutput::Success(Box::new(DetailedItem {
257 info: ItemInfo {
258 id: "1".to_string(),
259 name: "test".to_string(),
260 kind: "function".to_string(),
261 path: vec![],
262 docs: None,
263 visibility: "public".to_string(),
264 },
265 signature: Some("fn test()".to_string()),
266 generics: None,
267 fields: None,
268 variants: None,
269 methods: None,
270 source_location: None,
271 }));
272
273 assert!(success.is_success());
274 assert!(!success.is_error());
275
276 let error = GetItemDetailsOutput::Error {
277 error: "Not found".to_string(),
278 };
279
280 assert!(!error.is_success());
281 assert!(error.is_error());
282 }
283}