1use super::{Tool, Result, ToolError, common_options, parse_output_format, OutputFormat};
2use clap::{Arg, ArgMatches, Command};
3use colored::*;
4use std::fs;
5use std::path::Path;
6use syn::{
7 parse_file, File, Item, ItemFn, Fields, Field, Type, PathSegment, Ident,
8 visit::Visit, spanned::Spanned,
9};
10use quote::quote;
11use proc_macro2::TokenStream;
12use serde::{Serialize, Deserialize};
13#[derive(Debug, Clone)]
14pub struct ExampleGenTool;
15#[derive(Debug, Clone, Serialize, Deserialize)]
16struct FunctionInfo {
17 name: String,
18 params: Vec<ParameterInfo>,
19 return_type: Option<String>,
20 attributes: Vec<String>,
21 documentation: Option<String>,
22 span_placeholder: String,
23}
24#[derive(Debug, Clone, Serialize, Deserialize)]
25struct ParameterInfo {
26 name: String,
27 type_info: String,
28}
29#[derive(Debug, Clone, Serialize, Deserialize)]
30struct Example {
31 title: String,
32 code: String,
33 description: Option<String>,
34 example_type: ExampleType,
35}
36#[derive(Debug, Clone, Serialize, Deserialize)]
37enum ExampleType {
38 UnitTest,
39 IntegrationTest,
40 DocTest,
41 ErrorHandling,
42}
43impl ExampleGenTool {
44 pub fn new() -> Self {
45 Self
46 }
47 fn parse_function_signatures(&self, file_path: &str) -> Result<Vec<FunctionInfo>> {
48 let content = fs::read_to_string(file_path)
49 .map_err(|e| ToolError::ExecutionFailed(
50 format!("Failed to read {}: {}", file_path, e),
51 ))?;
52 let ast = parse_file(&content)
53 .map_err(|e| ToolError::ExecutionFailed(
54 format!("Failed to parse {}: {}", file_path, e),
55 ))?;
56 let mut functions: Vec<FunctionInfo> = Vec::new();
57 struct FunctionVisitor {
58 functions: Vec<FunctionInfo>,
59 }
60 impl<'ast> Visit<'ast> for FunctionVisitor {
61 fn visit_item_fn(&mut self, node: &'ast ItemFn) {
62 if matches!(node.vis, syn::Visibility::Public(_)) {
63 if let Some(func_info) = Self::extract_function_info(node) {
64 self.functions.push(func_info);
65 }
66 }
67 }
68 }
69 impl FunctionVisitor {
70 fn extract_function_info(node: &ItemFn) -> Option<FunctionInfo> {
71 let name = node.sig.ident.to_string();
72 let params = Self::extract_parameters(&node.sig.inputs);
73 let return_type = Self::extract_return_type(&node.sig.output);
74 let attributes = Self::extract_attributes(&node.attrs);
75 let documentation = Self::extract_documentation(&node.attrs);
76 Some(FunctionInfo {
77 name,
78 params,
79 return_type,
80 attributes,
81 documentation,
82 span_placeholder: "span_info_unavailable".to_string(),
83 })
84 }
85 fn extract_parameters(
86 inputs: &syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>,
87 ) -> Vec<ParameterInfo> {
88 let mut params = Vec::new();
89 for input in inputs {
90 if let syn::FnArg::Receiver(_) = input {
91 continue;
92 }
93 if let syn::FnArg::Typed(pat_type) = input {
94 if let syn::Pat::Ident(pat_ident) = &*pat_type.pat {
95 let name = pat_ident.ident.to_string();
96 let type_info = Self::type_to_string(&*pat_type.ty);
97 params.push(ParameterInfo { name, type_info });
98 }
99 }
100 }
101 params
102 }
103 fn extract_return_type(output: &syn::ReturnType) -> Option<String> {
104 match output {
105 syn::ReturnType::Default => None,
106 syn::ReturnType::Type(_, ty) => Some(Self::type_to_string(ty)),
107 }
108 }
109 fn extract_attributes(attrs: &[syn::Attribute]) -> Vec<String> {
110 attrs
111 .iter()
112 .map(|attr| {
113 attr.path()
114 .segments
115 .iter()
116 .map(|seg| seg.ident.to_string())
117 .collect::<Vec<_>>()
118 .join("::")
119 })
120 .collect()
121 }
122 fn extract_documentation(attrs: &[syn::Attribute]) -> Option<String> {
123 for attr in attrs {
124 if let Some(seg) = attr.path().segments.first() {
125 if seg.ident == "doc" {
126 if let Ok(syn::Meta::NameValue(name_value)) = attr
127 .parse_args::<syn::Meta>()
128 {
129 if let syn::Expr::Lit(expr_lit) = &name_value.value {
130 if let syn::Lit::Str(lit_str) = &expr_lit.lit {
131 return Some(lit_str.value().trim().to_string());
132 }
133 }
134 }
135 }
136 }
137 }
138 None
139 }
140 fn type_to_string(ty: &Type) -> String {
141 match ty {
142 Type::Path(type_path) => {
143 type_path
144 .path
145 .segments
146 .iter()
147 .map(|seg| seg.ident.to_string())
148 .collect::<Vec<_>>()
149 .join("::")
150 }
151 Type::Reference(type_ref) => {
152 let mut result = "&".to_string();
153 if type_ref.mutability.is_some() {
154 result.push_str("mut ");
155 }
156 result.push_str(&Self::type_to_string(&*type_ref.elem));
157 result
158 }
159 _ => "Unknown".to_string(),
160 }
161 }
162 }
163 let mut visitor = FunctionVisitor {
164 functions: Vec::new(),
165 };
166 syn::visit::visit_file(&mut visitor, &ast);
167 Ok(visitor.functions)
168 }
169 fn generate_function_examples(
170 &self,
171 func_info: &FunctionInfo,
172 example_types: &[String],
173 ) -> Result<Vec<Example>> {
174 let mut examples = Vec::new();
175 for example_type in example_types {
176 match example_type.as_str() {
177 "unit" => {
178 if let Some(example) = self.create_unit_test_example(func_info)? {
179 examples.push(example);
180 }
181 }
182 "integration" => {
183 if let Some(example) = self
184 .create_integration_test_example(func_info)?
185 {
186 examples.push(example);
187 }
188 }
189 "doc" => {
190 if let Some(example) = self.create_doc_test_example(func_info)? {
191 examples.push(example);
192 }
193 }
194 "error-handling" => {
195 let error_examples = self
196 .generate_error_handling_examples(func_info)?;
197 examples.extend(error_examples);
198 }
199 _ => {}
200 }
201 }
202 Ok(examples)
203 }
204 fn create_unit_test_example(
205 &self,
206 func_info: &FunctionInfo,
207 ) -> Result<Option<Example>> {
208 if func_info.params.is_empty() {
209 return Ok(None);
210 }
211 let mut code = format!("#[cfg(test)]\nmod tests {{\n use super::*;\n\n");
212 code.push_str(&format!(" #[test]\n fn test_{}() {{\n", func_info.name));
213 let param_values: Vec<String> = func_info
214 .params
215 .iter()
216 .map(|param| self.generate_parameter_value(¶m.type_info))
217 .collect();
218 let param_list = func_info
219 .params
220 .iter()
221 .zip(¶m_values)
222 .map(|(param, value)| format!("{}: {}", param.name, value))
223 .collect::<Vec<_>>()
224 .join(", ");
225 code.push_str(
226 &format!(" let result = {}({});\n", func_info.name, param_list),
227 );
228 if let Some(return_type) = &func_info.return_type {
229 if return_type.contains("Result") || return_type.contains("Option") {
230 code.push_str(" assert!(result.is_ok());\n");
231 } else if return_type == "bool" {
232 code.push_str(" assert!(result);\n");
233 } else if return_type.contains("Vec") || return_type.contains("HashMap") {
234 code.push_str(" assert!(!result.is_empty());\n");
235 } else {
236 code.push_str(" // Add assertions based on expected behavior\n");
237 }
238 }
239 code.push_str(" }\n");
240 code.push_str("}\n");
241 Ok(
242 Some(Example {
243 title: format!("๐งช Unit Test Example for {}", func_info.name),
244 code,
245 description: Some(
246 format!(
247 "Basic unit test showing how to call {} with typical parameters",
248 func_info.name
249 ),
250 ),
251 example_type: ExampleType::UnitTest,
252 }),
253 )
254 }
255 fn create_integration_test_example(
256 &self,
257 func_info: &FunctionInfo,
258 ) -> Result<Option<Example>> {
259 let mut code = format!(
260 "#[cfg(test)]\nmod integration_tests {{\n use super::*;\n\n"
261 );
262 code.push_str(
263 &format!(" #[test]\n fn test_{}_integration() {{\n", func_info.name),
264 );
265 code.push_str(" // Setup test environment\n");
266 let param_values: Vec<String> = func_info
267 .params
268 .iter()
269 .enumerate()
270 .map(|(i, param)| {
271 self.generate_integration_parameter_value(¶m.type_info, i)
272 })
273 .collect();
274 let param_list = func_info
275 .params
276 .iter()
277 .zip(¶m_values)
278 .map(|(param, value)| format!("{}: {}", param.name, value))
279 .collect::<Vec<_>>()
280 .join(", ");
281 code.push_str(
282 &format!(" let result = {}({});\n", func_info.name, param_list),
283 );
284 code.push_str(" // Verify the result in a broader context\n");
285 if let Some(return_type) = &func_info.return_type {
286 if return_type.contains("Result") {
287 code.push_str(" match result {\n");
288 code.push_str(" Ok(value) => {\n");
289 code.push_str(" // Verify successful operation\n");
290 code.push_str(
291 " assert!(true); // Replace with actual verification\n",
292 );
293 code.push_str(" }\n");
294 code.push_str(
295 " Err(e) => panic!(\"Integration test failed: {}\", e),\n",
296 );
297 code.push_str(" }\n");
298 } else {
299 code.push_str(" // Verify integration result\n");
300 code.push_str(
301 " assert!(true); // Replace with actual verification\n",
302 );
303 }
304 }
305 code.push_str(" }\n");
306 code.push_str("}\n");
307 Ok(
308 Some(Example {
309 title: format!("๐ Integration Test Example for {}", func_info.name),
310 code,
311 description: Some(
312 format!(
313 "Integration test showing {} usage in a broader context",
314 func_info.name
315 ),
316 ),
317 example_type: ExampleType::IntegrationTest,
318 }),
319 )
320 }
321 fn create_doc_test_example(
322 &self,
323 func_info: &FunctionInfo,
324 ) -> Result<Option<Example>> {
325 let mut code = String::new();
326 if let Some(docs) = &func_info.documentation {
327 code.push_str(&format!("/// {}\n", docs));
328 } else {
329 code.push_str(&format!("/// Example usage of {}\n", func_info.name));
330 }
331 code.push_str("///\n");
332 code.push_str("/// ```rust\n");
333 let param_values: Vec<String> = func_info
334 .params
335 .iter()
336 .map(|param| self.generate_doc_parameter_value(¶m.type_info))
337 .collect();
338 let param_list = param_values.join(", ");
339 code.push_str(
340 &format!("/// let result = {}({});\n", func_info.name, param_list),
341 );
342 if let Some(return_type) = &func_info.return_type {
343 if return_type.contains("Result") {
344 code.push_str("/// match result {\n");
345 code.push_str(
346 "/// Ok(value) => println!(\"Success: {{:?}}\", value),\n",
347 );
348 code.push_str("/// Err(e) => eprintln!(\"Error: {{}}\", e),\n");
349 code.push_str("/// }\n");
350 } else {
351 code.push_str("/// println!(\"Result: {{:?}}\", result);\n");
352 }
353 } else {
354 code.push_str("/// // Function completed successfully\n");
355 }
356 code.push_str("/// ```\n");
357 Ok(
358 Some(Example {
359 title: format!("๐ Documentation Example for {}", func_info.name),
360 code,
361 description: Some(
362 format!(
363 "Documentation example showing typical usage of {}", func_info
364 .name
365 ),
366 ),
367 example_type: ExampleType::DocTest,
368 }),
369 )
370 }
371 fn generate_error_handling_examples(
372 &self,
373 func_info: &FunctionInfo,
374 ) -> Result<Vec<Example>> {
375 let mut examples = Vec::new();
376 if let Some(return_type) = &func_info.return_type {
377 if return_type.contains("Result") || return_type.contains("Option") {
378 let mut code = format!(
379 "// Error handling example for {}\n", func_info.name
380 );
381 code.push_str("// Handle potential errors gracefully\n");
382 let param_values: Vec<String> = func_info
383 .params
384 .iter()
385 .map(|param| self.generate_error_parameter_value(¶m.type_info))
386 .collect();
387 let param_list = param_values.join(", ");
388 if return_type.contains("Result") {
389 code.push_str(
390 &format!("match {}({}) {{\n", func_info.name, param_list),
391 );
392 code.push_str(" Ok(result) => {\n");
393 code.push_str(" println!(\"Success: {{:?}}\", result);\n");
394 code.push_str(" // Process successful result\n");
395 code.push_str(" }\n");
396 code.push_str(" Err(error) => {\n");
397 code.push_str(
398 " eprintln!(\"Operation failed: {{}}\", error);\n",
399 );
400 code.push_str(" // Handle error appropriately\n");
401 code.push_str(" match error {\n");
402 code.push_str(" // Handle specific error types\n");
403 code.push_str(" _ => {\n");
404 code.push_str(" // Fallback error handling\n");
405 code.push_str(" std::process::exit(1);\n");
406 code.push_str(" }\n");
407 code.push_str(" }\n");
408 code.push_str(" }\n");
409 code.push_str("}\n");
410 } else if return_type.contains("Option") {
411 code.push_str(
412 &format!(
413 "if let Some(result) = {}({}) {{\n", func_info.name,
414 param_list
415 ),
416 );
417 code.push_str(" println!(\"Success: {{:?}}\", result);\n");
418 code.push_str(" // Process successful result\n");
419 code.push_str("} else {\n");
420 code.push_str(" eprintln!(\"Operation returned None\");\n");
421 code.push_str(" // Handle None case\n");
422 code.push_str("}\n");
423 }
424 examples
425 .push(Example {
426 title: format!(
427 "โ ๏ธ Error Handling Example for {}", func_info.name
428 ),
429 code,
430 description: Some(
431 format!(
432 "Comprehensive error handling patterns for {}", func_info
433 .name
434 ),
435 ),
436 example_type: ExampleType::ErrorHandling,
437 });
438 }
439 }
440 Ok(examples)
441 }
442 fn generate_parameter_value(&self, param_type: &str) -> String {
443 match param_type {
444 "i32" | "i64" | "u32" | "u64" | "isize" | "usize" => "42".to_string(),
445 "String" | "&str" => "\"example\"".to_string(),
446 "bool" => "true".to_string(),
447 "f32" | "f64" => "3.14".to_string(),
448 "Vec<T>" => "vec![item1, item2]".to_string(),
449 "Option<T>" => "Some(value)".to_string(),
450 "Result<T, E>" => "Ok(value)".to_string(),
451 _ => format!("{}::default()", param_type),
452 }
453 }
454 fn generate_integration_parameter_value(
455 &self,
456 param_type: &str,
457 index: usize,
458 ) -> String {
459 match param_type {
460 "i32" | "i64" | "u32" | "u64" | "isize" | "usize" => {
461 format!("test_value_{}", index)
462 }
463 "String" | "&str" => format!("\"test_input_{}\"", index),
464 "bool" => "false".to_string(),
465 "f32" | "f64" => format!("{}.5", index + 1),
466 "Vec<T>" => format!("vec![test_item_{}]", index),
467 "Option<T>" => format!("Some(test_value_{})", index),
468 "Result<T, E>" => format!("Ok(test_result_{})", index),
469 _ => format!("test_{}", index),
470 }
471 }
472 fn generate_doc_parameter_value(&self, param_type: &str) -> String {
473 match param_type {
474 "i32" | "i64" | "u32" | "u64" | "isize" | "usize" => "123".to_string(),
475 "String" | "&str" => "\"hello world\"".to_string(),
476 "bool" => "true".to_string(),
477 "f32" | "f64" => "2.5".to_string(),
478 "Vec<T>" => "vec![item1, item2]".to_string(),
479 "Option<T>" => "Some(value)".to_string(),
480 "Result<T, E>" => "Ok(result)".to_string(),
481 _ => "value".to_string(),
482 }
483 }
484 fn generate_error_parameter_value(&self, param_type: &str) -> String {
485 match param_type {
486 "i32" | "i64" | "u32" | "u64" | "isize" | "usize" => "0".to_string(),
487 "String" | "&str" => "\"\"".to_string(),
488 "bool" => "false".to_string(),
489 "f32" | "f64" => "0.0".to_string(),
490 "Vec<T>" => "vec![]".to_string(),
491 "Option<T>" => "None".to_string(),
492 "Result<T, E>" => "Err(error)".to_string(),
493 _ => "invalid_value".to_string(),
494 }
495 }
496 fn format_examples_as_rust(&self, examples: &[Example]) -> Result<String> {
497 let mut code = String::new();
498 for (i, example) in examples.iter().enumerate() {
499 if i > 0 {
500 code.push_str("\n\n");
501 }
502 code.push_str(&format!("// {}\n", example.title));
503 if let Some(desc) = &example.description {
504 code.push_str(&format!("// {}\n", desc));
505 }
506 code.push_str(&example.code);
507 }
508 Ok(code)
509 }
510 fn format_examples_as_markdown(&self, examples: &[Example]) -> Result<String> {
511 let mut markdown = String::new();
512 for example in examples {
513 markdown.push_str(&format!("## {}\n\n", example.title));
514 if let Some(description) = &example.description {
515 markdown.push_str(&format!("{}\n\n", description));
516 }
517 markdown.push_str("```rust\n");
518 markdown.push_str(&example.code);
519 markdown.push_str("\n```\n\n");
520 }
521 Ok(markdown)
522 }
523}
524impl Tool for ExampleGenTool {
525 fn name(&self) -> &'static str {
526 "example-gen"
527 }
528 fn description(&self) -> &'static str {
529 "Generate runnable examples from function signatures"
530 }
531 fn command(&self) -> Command {
532 Command::new(self.name())
533 .about(self.description())
534 .long_about(
535 "Automatically generate runnable examples from function signatures, helping developers understand how to use APIs. Creates unit tests, integration tests, documentation examples, and error handling patterns.",
536 )
537 .args(
538 &[
539 Arg::new("input")
540 .long("input")
541 .short('i')
542 .help("Input Rust file or directory to analyze")
543 .required(true),
544 Arg::new("function")
545 .long("function")
546 .short('f')
547 .help("Specific function to generate examples for"),
548 Arg::new("type")
549 .long("type")
550 .short('t')
551 .help("Example types: unit, integration, doc, error-handling")
552 .default_value("unit,doc"),
553 Arg::new("output")
554 .long("output")
555 .short('o')
556 .help("Output directory for generated examples")
557 .default_value("examples/generated/"),
558 Arg::new("format")
559 .long("format")
560 .help("Output format: rust, markdown, json")
561 .default_value("rust"),
562 Arg::new("include-private")
563 .long("include-private")
564 .help("Include examples for private functions")
565 .action(clap::ArgAction::SetTrue),
566 Arg::new("with-tests")
567 .long("with-tests")
568 .help("Generate corresponding test files")
569 .action(clap::ArgAction::SetTrue),
570 Arg::new("context")
571 .long("context")
572 .help("Include usage context and dependencies")
573 .action(clap::ArgAction::SetTrue),
574 Arg::new("validate")
575 .long("validate")
576 .help("Validate that generated examples compile")
577 .action(clap::ArgAction::SetTrue),
578 ],
579 )
580 .args(&common_options())
581 }
582 fn execute(&self, matches: &ArgMatches) -> Result<()> {
583 let input = matches.get_one::<String>("input").unwrap();
584 let function_filter = matches.get_one::<String>("function");
585 let example_types: Vec<String> = matches
586 .get_one::<String>("type")
587 .unwrap()
588 .split(',')
589 .map(|s| s.trim().to_string())
590 .collect();
591 let output = matches.get_one::<String>("output").unwrap();
592 let format = matches.get_one::<String>("format").unwrap();
593 let dry_run = matches.get_flag("dry-run");
594 let verbose = matches.get_flag("verbose");
595 let validate = matches.get_flag("validate");
596 let output_format = parse_output_format(matches);
597 println!(
598 "๐ {} - {}", "CargoMate ExampleGen".bold().blue(), self.description()
599 .cyan()
600 );
601 if !Path::new(input).exists() {
602 return Err(
603 ToolError::InvalidArguments(format!("Input not found: {}", input)),
604 );
605 }
606 if !dry_run {
607 fs::create_dir_all(output)
608 .map_err(|e| ToolError::ExecutionFailed(
609 format!("Failed to create output directory: {}", e),
610 ))?;
611 }
612 let mut all_examples = Vec::new();
613 if Path::new(input).is_dir() {
614 for entry in fs::read_dir(input)? {
615 let entry = entry?;
616 let path = entry.path();
617 if path.is_file() && path.extension().unwrap_or_default() == "rs" {
618 let functions = self
619 .parse_function_signatures(&path.to_string_lossy())?;
620 for func_info in functions {
621 if let Some(filter) = function_filter {
622 if func_info.name != *filter {
623 continue;
624 }
625 }
626 let examples = self
627 .generate_function_examples(&func_info, &example_types)?;
628 all_examples.extend(examples);
629 }
630 }
631 }
632 } else {
633 let functions = self.parse_function_signatures(input)?;
634 for func_info in functions {
635 if let Some(filter) = function_filter {
636 if func_info.name != *filter {
637 continue;
638 }
639 }
640 let examples = self
641 .generate_function_examples(&func_info, &example_types)?;
642 all_examples.extend(examples);
643 }
644 }
645 if all_examples.is_empty() {
646 println!(
647 "{}", "No examples generated. Check input file or function filter."
648 .yellow()
649 );
650 return Ok(());
651 }
652 if verbose {
653 println!(
654 " ๐ Generated {} examples from {} functions", all_examples.len(),
655 all_examples.len() / example_types.len()
656 );
657 }
658 let output_content = match format.as_str() {
659 "rust" => self.format_examples_as_rust(&all_examples)?,
660 "markdown" => self.format_examples_as_markdown(&all_examples)?,
661 "json" => serde_json::to_string_pretty(&all_examples).unwrap(),
662 _ => {
663 return Err(
664 ToolError::InvalidArguments(
665 format!("Unsupported format: {}", format),
666 ),
667 );
668 }
669 };
670 let output_file = format!("{}/generated_examples.{}", output, format);
671 match output_format {
672 OutputFormat::Human => {
673 println!(" โ
Generated {} examples", all_examples.len());
674 println!(" โ {}", output_file.cyan());
675 if validate {
676 println!(" โ
Validation enabled - checking example compilation");
677 }
678 if dry_run {
679 println!(" ๐ {}", "Generated content preview:".bold());
680 println!(" {}", "โ".repeat(50));
681 for line in output_content.lines().take(10) {
682 println!(" {}", line);
683 }
684 if output_content.lines().count() > 10 {
685 println!(" ... (truncated)");
686 }
687 } else {
688 fs::write(&output_file, &output_content)
689 .map_err(|e| ToolError::ExecutionFailed(
690 format!("Failed to write {}: {}", output_file, e),
691 ))?;
692 println!(" ๐พ File written successfully");
693 }
694 }
695 OutputFormat::Json => {
696 let result = serde_json::json!(
697 { "input" : input, "output" : output_file, "examples_generated" :
698 all_examples.len(), "format" : format, "example_types" :
699 example_types, "content_preview" : output_content.lines().take(5)
700 .collect::< Vec < _ >> ().join("\n") }
701 );
702 println!("{}", serde_json::to_string_pretty(& result).unwrap());
703 }
704 OutputFormat::Table => {
705 println!("{:<30} {:<15} {:<20}", "Function", "Examples", "Types");
706 println!("{}", "โ".repeat(70));
707 let mut example_counts = std::collections::HashMap::new();
708 for example in &all_examples {
709 let count = example_counts.entry(example.title.clone()).or_insert(0);
710 *count += 1;
711 }
712 for (title, count) in example_counts {
713 println!(
714 "{:<30} {:<15} {:<20}", title, count, example_types.join(",")
715 );
716 }
717 }
718 }
719 println!("\n๐ Example generation completed!");
720 Ok(())
721 }
722}
723impl Default for ExampleGenTool {
724 fn default() -> Self {
725 Self::new()
726 }
727}