rust_docs_mcp/analysis/
outputs.rs

1//! Output types for analysis tools
2//!
3//! These types are used as the return values from analysis 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};
8
9/// Enhanced node structure for crate structure analysis
10#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
11pub struct StructureNode {
12    pub kind: String,
13    pub name: String,
14    pub path: String,
15    pub visibility: String,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub children: Option<Vec<StructureNode>>,
18}
19
20/// Output from structure (analyze_crate_structure) operation
21#[derive(Debug, Serialize, Deserialize, PartialEq)]
22pub struct StructureOutput {
23    pub status: String,
24    pub message: String,
25    pub tree: StructureNode,
26    pub usage_hint: String,
27}
28
29impl StructureOutput {
30    /// Convert to JSON string for MCP response
31    pub fn to_json(&self) -> String {
32        serde_json::to_string(self)
33            .unwrap_or_else(|_| r#"{"error":"Failed to serialize response"}"#.to_string())
34    }
35
36    /// Check if this is a success response
37    pub fn is_success(&self) -> bool {
38        self.status == "success"
39    }
40}
41
42/// Error output for analysis tools
43#[derive(Debug, Serialize, Deserialize, PartialEq)]
44pub struct AnalysisErrorOutput {
45    pub error: String,
46}
47
48impl AnalysisErrorOutput {
49    /// Create a new error output
50    pub fn new(message: impl Into<String>) -> Self {
51        Self {
52            error: message.into(),
53        }
54    }
55
56    /// Convert to JSON string for MCP response
57    pub fn to_json(&self) -> String {
58        serde_json::to_string(self)
59            .unwrap_or_else(|_| r#"{"error":"Failed to serialize error"}"#.to_string())
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_structure_output_serialization() {
69        let output = StructureOutput {
70            status: "success".to_string(),
71            message: "Module structure analysis completed".to_string(),
72            tree: StructureNode {
73                kind: "module".to_string(),
74                name: "root".to_string(),
75                path: "".to_string(),
76                visibility: "public".to_string(),
77                children: Some(vec![StructureNode {
78                    kind: "struct".to_string(),
79                    name: "MyStruct".to_string(),
80                    path: "my_mod".to_string(),
81                    visibility: "public".to_string(),
82                    children: None,
83                }]),
84            },
85            usage_hint: "Use the 'path' and 'name' fields to search for items".to_string(),
86        };
87
88        assert!(output.is_success());
89
90        let json = output.to_json();
91        let deserialized: StructureOutput = serde_json::from_str(&json).unwrap();
92        assert_eq!(output, deserialized);
93    }
94
95    #[test]
96    fn test_analysis_error_output() {
97        let output = AnalysisErrorOutput::new("Failed to analyze crate");
98        let json = output.to_json();
99        let deserialized: AnalysisErrorOutput = serde_json::from_str(&json).unwrap();
100        assert_eq!(output, deserialized);
101    }
102}