rust_docs_mcp/cache/
outputs.rs

1//! Output types for cache operations
2//!
3//! These types are used as the return values from cache tool methods.
4//! They are serialized to JSON strings for the MCP protocol, and can be
5//! deserialized in tests for type-safe validation.
6
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10/// Output from cache_crate operations (crates.io, GitHub, local)
11#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
12#[serde(tag = "status")]
13pub enum CacheCrateOutput {
14    /// Successful caching operation
15    #[serde(rename = "success")]
16    Success {
17        message: String,
18        #[serde(rename = "crate")]
19        crate_name: String,
20        version: String,
21        #[serde(skip_serializing_if = "Option::is_none")]
22        members: Option<Vec<String>>,
23        #[serde(skip_serializing_if = "Option::is_none")]
24        results: Option<Vec<String>>,
25        #[serde(skip_serializing_if = "Option::is_none")]
26        updated: Option<bool>,
27    },
28    /// Partial success when caching workspace members
29    #[serde(rename = "partial_success")]
30    PartialSuccess {
31        message: String,
32        #[serde(rename = "crate")]
33        crate_name: String,
34        version: String,
35        members: Vec<String>,
36        results: Vec<String>,
37        errors: Vec<String>,
38        #[serde(skip_serializing_if = "Option::is_none")]
39        updated: Option<bool>,
40    },
41    /// Workspace detected, needs member specification
42    #[serde(rename = "workspace_detected")]
43    WorkspaceDetected {
44        message: String,
45        #[serde(rename = "crate")]
46        crate_name: String,
47        version: String,
48        workspace_members: Vec<String>,
49        example_usage: String,
50        #[serde(skip_serializing_if = "Option::is_none")]
51        updated: Option<bool>,
52    },
53    /// Error occurred during operation
54    #[serde(rename = "error")]
55    Error { error: String },
56}
57
58impl CacheCrateOutput {
59    /// Convert to JSON string for MCP response
60    pub fn to_json(&self) -> String {
61        serde_json::to_string(self)
62            .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
63    }
64
65    /// Check if this is a success response
66    pub fn is_success(&self) -> bool {
67        matches!(self, CacheCrateOutput::Success { .. })
68    }
69
70    /// Check if this is an error response
71    pub fn is_error(&self) -> bool {
72        matches!(self, CacheCrateOutput::Error { .. })
73    }
74
75    /// Check if this is a workspace detection response
76    pub fn is_workspace_detected(&self) -> bool {
77        matches!(self, CacheCrateOutput::WorkspaceDetected { .. })
78    }
79}
80
81/// Output from remove_crate operation
82#[derive(Debug, Serialize, Deserialize, PartialEq)]
83pub struct RemoveCrateOutput {
84    pub status: String,
85    pub message: String,
86    #[serde(rename = "crate")]
87    pub crate_name: String,
88    pub version: String,
89}
90
91impl RemoveCrateOutput {
92    /// Convert to JSON string for MCP response
93    pub fn to_json(&self) -> String {
94        serde_json::to_string(self)
95            .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
96    }
97}
98
99/// Version information for a cached crate
100#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
101pub struct VersionInfo {
102    pub version: String,
103    pub cached_at: String,
104    pub doc_generated: bool,
105    pub size_bytes: u64,
106    pub size_human: String,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub members: Option<Vec<String>>,
109}
110
111/// Size information with human-readable format
112#[derive(Debug, Serialize, Deserialize, PartialEq)]
113pub struct SizeInfo {
114    pub bytes: u64,
115    pub human: String,
116}
117
118/// Output from list_cached_crates operation
119#[derive(Debug, Serialize, Deserialize, PartialEq)]
120pub struct ListCachedCratesOutput {
121    pub crates: HashMap<String, Vec<VersionInfo>>,
122    pub total_crates: usize,
123    pub total_versions: usize,
124    pub total_size: SizeInfo,
125}
126
127impl ListCachedCratesOutput {
128    /// Convert to JSON string for MCP response
129    pub fn to_json(&self) -> String {
130        serde_json::to_string(self)
131            .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
132    }
133}
134
135/// Output from list_crate_versions operation
136#[derive(Debug, Serialize, Deserialize, PartialEq)]
137pub struct ListCrateVersionsOutput {
138    #[serde(rename = "crate")]
139    pub crate_name: String,
140    pub versions: Vec<VersionInfo>,
141    pub count: usize,
142}
143
144impl ListCrateVersionsOutput {
145    /// Convert to JSON string for MCP response
146    pub fn to_json(&self) -> String {
147        serde_json::to_string(self)
148            .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
149    }
150}
151
152/// Metadata for a single crate
153#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
154pub struct CrateMetadata {
155    pub crate_name: String,
156    pub version: String,
157    pub cached: bool,
158    pub analyzed: bool,
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub cache_size_bytes: Option<u64>,
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub cache_size_human: Option<String>,
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub member: Option<String>,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub workspace_members: Option<Vec<String>>,
167}
168
169/// Output from get_crates_metadata operation
170#[derive(Debug, Serialize, Deserialize, PartialEq)]
171pub struct GetCratesMetadataOutput {
172    pub metadata: Vec<CrateMetadata>,
173    pub total_queried: usize,
174    pub total_cached: usize,
175}
176
177impl GetCratesMetadataOutput {
178    /// Convert to JSON string for MCP response
179    pub fn to_json(&self) -> String {
180        serde_json::to_string(self)
181            .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
182    }
183}
184
185/// Generic error output that can be used by any tool
186#[derive(Debug, Serialize, Deserialize, PartialEq)]
187pub struct ErrorOutput {
188    pub error: String,
189}
190
191impl ErrorOutput {
192    /// Create a new error output
193    pub fn new(message: impl Into<String>) -> Self {
194        Self {
195            error: message.into(),
196        }
197    }
198
199    /// Convert to JSON string for MCP response
200    pub fn to_json(&self) -> String {
201        serde_json::to_string(self)
202            .unwrap_or_else(|_| r#"{"error":"Failed to serialize error"}"#.to_string())
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209
210    #[test]
211    fn test_cache_crate_output_serialization() {
212        let output = CacheCrateOutput::Success {
213            message: "Successfully cached test-crate-1.0.0".to_string(),
214            crate_name: "test-crate".to_string(),
215            version: "1.0.0".to_string(),
216            members: None,
217            results: None,
218            updated: None,
219        };
220
221        let json = output.to_json();
222        let deserialized: CacheCrateOutput = serde_json::from_str(&json).unwrap();
223        assert_eq!(output, deserialized);
224        assert!(deserialized.is_success());
225    }
226
227    #[test]
228    fn test_workspace_detected_output() {
229        let output = CacheCrateOutput::WorkspaceDetected {
230            message: "This is a workspace crate".to_string(),
231            crate_name: "workspace".to_string(),
232            version: "1.0.0".to_string(),
233            workspace_members: vec!["member1".to_string(), "member2".to_string()],
234            example_usage: "example".to_string(),
235            updated: None,
236        };
237
238        assert!(output.is_workspace_detected());
239        assert!(!output.is_success());
240        assert!(!output.is_error());
241    }
242
243    #[test]
244    fn test_error_output() {
245        let output = ErrorOutput::new("Something went wrong");
246        let json = output.to_json();
247        let deserialized: ErrorOutput = serde_json::from_str(&json).unwrap();
248        assert_eq!(output, deserialized);
249    }
250}