mcp_execution_codegen/common/types.rs
1//! Types for code generation.
2//!
3//! Defines the data structures used during code generation from MCP
4//! tool schemas to executable TypeScript or Rust code.
5//!
6//! # Examples
7//!
8//! ```
9//! use mcp_execution_codegen::{GeneratedCode, GeneratedFile};
10//!
11//! let file = GeneratedFile {
12//! path: "tools/sendMessage.ts".to_string(),
13//! content: "export function sendMessage() {}".to_string(),
14//! };
15//!
16//! let code = GeneratedCode {
17//! files: vec![file],
18//! };
19//!
20//! assert_eq!(code.files.len(), 1);
21//! ```
22
23use serde::{Deserialize, Serialize};
24use std::collections::HashMap;
25
26/// Result of code generation containing all generated files.
27///
28/// This is the main output type returned by the code generator.
29/// Contains a list of files that should be written to disk.
30///
31/// # Examples
32///
33/// ```
34/// use mcp_execution_codegen::GeneratedCode;
35///
36/// let code = GeneratedCode {
37/// files: vec![],
38/// };
39///
40/// assert_eq!(code.file_count(), 0);
41/// ```
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct GeneratedCode {
44 /// List of generated files with paths and contents
45 pub files: Vec<GeneratedFile>,
46}
47
48impl GeneratedCode {
49 /// Creates a new empty generated code container.
50 ///
51 /// # Examples
52 ///
53 /// ```
54 /// use mcp_execution_codegen::GeneratedCode;
55 ///
56 /// let code = GeneratedCode::new();
57 /// assert_eq!(code.file_count(), 0);
58 /// ```
59 #[inline]
60 #[must_use]
61 pub fn new() -> Self {
62 Self { files: Vec::new() }
63 }
64
65 /// Adds a generated file to the collection.
66 ///
67 /// # Examples
68 ///
69 /// ```
70 /// use mcp_execution_codegen::{GeneratedCode, GeneratedFile};
71 ///
72 /// let mut code = GeneratedCode::new();
73 /// code.add_file(GeneratedFile {
74 /// path: "index.ts".to_string(),
75 /// content: "export {}".to_string(),
76 /// });
77 ///
78 /// assert_eq!(code.file_count(), 1);
79 /// ```
80 pub fn add_file(&mut self, file: GeneratedFile) {
81 self.files.push(file);
82 }
83
84 /// Returns the number of generated files.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// use mcp_execution_codegen::GeneratedCode;
90 ///
91 /// let code = GeneratedCode::new();
92 /// assert_eq!(code.file_count(), 0);
93 /// ```
94 #[inline]
95 #[must_use]
96 pub fn file_count(&self) -> usize {
97 self.files.len()
98 }
99
100 /// Returns an iterator over the generated files.
101 ///
102 /// # Examples
103 ///
104 /// ```
105 /// use mcp_execution_codegen::{GeneratedCode, GeneratedFile};
106 ///
107 /// let mut code = GeneratedCode::new();
108 /// code.add_file(GeneratedFile {
109 /// path: "test.ts".to_string(),
110 /// content: "content".to_string(),
111 /// });
112 ///
113 /// for file in code.files() {
114 /// println!("Path: {}", file.path);
115 /// }
116 /// ```
117 #[inline]
118 pub fn files(&self) -> impl Iterator<Item = &GeneratedFile> {
119 self.files.iter()
120 }
121}
122
123impl Default for GeneratedCode {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129/// A single generated file with path and content.
130///
131/// Represents one file that will be written to the virtual filesystem
132/// or actual filesystem during code generation.
133///
134/// # Examples
135///
136/// ```
137/// use mcp_execution_codegen::GeneratedFile;
138///
139/// let file = GeneratedFile {
140/// path: "types.ts".to_string(),
141/// content: "export type Params = {};".to_string(),
142/// };
143///
144/// assert_eq!(file.path, "types.ts");
145/// ```
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct GeneratedFile {
148 /// Relative path where the file should be written
149 pub path: String,
150 /// File content
151 pub content: String,
152}
153
154impl GeneratedFile {
155 /// Returns the file path.
156 ///
157 /// # Examples
158 ///
159 /// ```
160 /// use mcp_execution_codegen::GeneratedFile;
161 ///
162 /// let file = GeneratedFile {
163 /// path: "test.ts".to_string(),
164 /// content: String::new(),
165 /// };
166 ///
167 /// assert_eq!(file.path(), "test.ts");
168 /// ```
169 #[inline]
170 #[must_use]
171 pub fn path(&self) -> &str {
172 &self.path
173 }
174
175 /// Returns the file content.
176 ///
177 /// # Examples
178 ///
179 /// ```
180 /// use mcp_execution_codegen::GeneratedFile;
181 ///
182 /// let file = GeneratedFile {
183 /// path: "test.ts".to_string(),
184 /// content: "export {}".to_string(),
185 /// };
186 ///
187 /// assert_eq!(file.content(), "export {}");
188 /// ```
189 #[inline]
190 #[must_use]
191 pub fn content(&self) -> &str {
192 &self.content
193 }
194}
195
196/// Template context for code generation.
197///
198/// Contains all the data needed to render a Handlebars template.
199/// This is typically constructed from MCP server information.
200///
201/// # Examples
202///
203/// ```
204/// use mcp_execution_codegen::TemplateContext;
205/// use std::collections::HashMap;
206///
207/// let context = TemplateContext {
208/// server_name: "github".to_string(),
209/// server_version: "1.0.0".to_string(),
210/// tools: vec![],
211/// metadata: HashMap::new(),
212/// };
213///
214/// assert_eq!(context.server_name, "github");
215/// ```
216#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct TemplateContext {
218 /// Name of the MCP server
219 pub server_name: String,
220 /// Server version string
221 pub server_version: String,
222 /// List of tool definitions
223 pub tools: Vec<ToolDefinition>,
224 /// Additional metadata for template rendering
225 pub metadata: HashMap<String, serde_json::Value>,
226}
227
228/// Definition of a single MCP tool for code generation.
229///
230/// Contains all information needed to generate TypeScript or Rust
231/// code for calling an MCP tool.
232///
233/// # Examples
234///
235/// ```
236/// use mcp_execution_codegen::ToolDefinition;
237/// use serde_json::json;
238///
239/// let tool = ToolDefinition {
240/// name: "send_message".to_string(),
241/// description: "Sends a message".to_string(),
242/// input_schema: json!({"type": "object"}),
243/// typescript_name: "sendMessage".to_string(),
244/// };
245///
246/// assert_eq!(tool.name, "send_message");
247/// assert_eq!(tool.typescript_name, "sendMessage");
248/// ```
249#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct ToolDefinition {
251 /// Original tool name (snake_case from MCP)
252 pub name: String,
253 /// Human-readable description
254 pub description: String,
255 /// JSON Schema for input parameters
256 pub input_schema: serde_json::Value,
257 /// TypeScript-friendly name (camelCase)
258 pub typescript_name: String,
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_generated_code_new() {
267 let code = GeneratedCode::new();
268 assert_eq!(code.file_count(), 0);
269 }
270
271 #[test]
272 fn test_generated_code_default() {
273 let code = GeneratedCode::default();
274 assert_eq!(code.file_count(), 0);
275 }
276
277 #[test]
278 fn test_add_file() {
279 let mut code = GeneratedCode::new();
280 code.add_file(GeneratedFile {
281 path: "test.ts".to_string(),
282 content: "content".to_string(),
283 });
284 assert_eq!(code.file_count(), 1);
285 }
286
287 #[test]
288 fn test_tool_definition() {
289 let tool = ToolDefinition {
290 name: "test_tool".to_string(),
291 description: "Test".to_string(),
292 input_schema: serde_json::json!({"type": "object"}),
293 typescript_name: "testTool".to_string(),
294 };
295
296 assert_eq!(tool.name, "test_tool");
297 assert_eq!(tool.typescript_name, "testTool");
298 }
299
300 #[test]
301 fn test_template_context() {
302 let context = TemplateContext {
303 server_name: "test-server".to_string(),
304 server_version: "1.0.0".to_string(),
305 tools: vec![],
306 metadata: HashMap::new(),
307 };
308
309 assert_eq!(context.server_name, "test-server");
310 assert_eq!(context.tools.len(), 0);
311 }
312}