sklears_core/
api_formatters.rs

1//! Output Formatters and Document Generators
2//!
3//! This module provides comprehensive formatting capabilities for API documentation,
4//! supporting multiple output formats including JSON, HTML, Markdown, and interactive
5//! web-based documentation with live code examples.
6
7use crate::api_analyzers::{CrossReferenceBuilder, ExampleValidator, TraitAnalyzer, TypeExtractor};
8use crate::api_data_structures::{
9    ApiMetadata, ApiReference, CodeExample, CrateInfo, TraitInfo, TypeInfo,
10};
11use crate::api_generator_config::{GeneratorConfig, OutputFormat, ThemeConfig};
12use crate::error::{Result, SklearsError};
13use std::collections::HashMap;
14use std::path::PathBuf;
15
16// ================================================================================================
17// MAIN API REFERENCE GENERATOR
18// ================================================================================================
19
20/// Main API reference generator with comprehensive formatting capabilities
21#[derive(Debug)]
22pub struct ApiReferenceGenerator {
23    config: GeneratorConfig,
24    trait_analyzer: TraitAnalyzer,
25    type_extractor: TypeExtractor,
26    example_validator: ExampleValidator,
27    cross_ref_builder: CrossReferenceBuilder,
28    formatter: DocumentFormatter,
29}
30
31impl ApiReferenceGenerator {
32    /// Create a new API reference generator with the given configuration
33    pub fn new(config: GeneratorConfig) -> Self {
34        Self {
35            formatter: DocumentFormatter::new(config.clone()),
36            config: config.clone(),
37            trait_analyzer: TraitAnalyzer::new(config.clone()),
38            type_extractor: TypeExtractor::new(config.clone()),
39            example_validator: ExampleValidator::new(),
40            cross_ref_builder: CrossReferenceBuilder::new(),
41        }
42    }
43
44    /// Generate API reference from a Rust crate
45    pub fn generate_from_crate(&mut self, crate_name: &str) -> Result<ApiReference> {
46        let crate_info = self.analyze_crate(crate_name)?;
47
48        let traits = if self.config.include_type_info {
49            self.trait_analyzer.analyze_traits(&crate_info)?
50        } else {
51            Vec::new()
52        };
53
54        let types = if self.config.include_type_info {
55            self.type_extractor.extract_types(&crate_info)?
56        } else {
57            Vec::new()
58        };
59
60        let examples = if self.config.include_examples {
61            let raw_examples = self.extract_examples(&crate_info)?;
62            if self.config.validate_examples {
63                self.example_validator.validate_examples(&raw_examples)?
64            } else {
65                raw_examples
66            }
67        } else {
68            Vec::new()
69        };
70
71        let cross_refs = if self.config.include_cross_refs {
72            self.cross_ref_builder
73                .build_cross_references(&traits, &types)?
74        } else {
75            HashMap::new()
76        };
77
78        Ok(ApiReference {
79            crate_name: crate_name.to_string(),
80            version: crate_info.version.clone(),
81            traits,
82            types,
83            examples,
84            cross_references: cross_refs,
85            metadata: self.generate_metadata(&crate_info)?,
86        })
87    }
88
89    /// Generate API reference from source files
90    pub fn generate_from_files(&mut self, source_files: Vec<PathBuf>) -> Result<ApiReference> {
91        // Parse source files and extract information
92        let crate_info = self.analyze_source_files(&source_files)?;
93
94        // Generate API reference using the same logic as generate_from_crate
95        let traits = if self.config.include_type_info {
96            self.trait_analyzer.analyze_traits(&crate_info)?
97        } else {
98            Vec::new()
99        };
100
101        let types = if self.config.include_type_info {
102            self.type_extractor.extract_types(&crate_info)?
103        } else {
104            Vec::new()
105        };
106
107        let examples = if self.config.include_examples {
108            let raw_examples = self.extract_examples_from_files(&source_files)?;
109            if self.config.validate_examples {
110                self.example_validator.validate_examples(&raw_examples)?
111            } else {
112                raw_examples
113            }
114        } else {
115            Vec::new()
116        };
117
118        let cross_refs = if self.config.include_cross_refs {
119            self.cross_ref_builder
120                .build_cross_references(&traits, &types)?
121        } else {
122            HashMap::new()
123        };
124
125        Ok(ApiReference {
126            crate_name: "custom".to_string(),
127            version: "unknown".to_string(),
128            traits,
129            types,
130            examples,
131            cross_references: cross_refs,
132            metadata: self.generate_metadata(&crate_info)?,
133        })
134    }
135
136    /// Format API reference to the configured output format
137    pub fn format_output(&self, api_ref: &ApiReference) -> Result<String> {
138        match self.config.output_format {
139            OutputFormat::Json => self.formatter.format_json(api_ref),
140            OutputFormat::Html => self.formatter.format_html(api_ref),
141            OutputFormat::Markdown => self.formatter.format_markdown(api_ref),
142            OutputFormat::Interactive => self.formatter.format_interactive(api_ref),
143            OutputFormat::OpenApi => self.formatter.format_openapi(api_ref),
144        }
145    }
146
147    /// Format API reference to a specific output format
148    pub fn format_as(&self, api_ref: &ApiReference, format: OutputFormat) -> Result<String> {
149        match format {
150            OutputFormat::Json => self.formatter.format_json(api_ref),
151            OutputFormat::Html => self.formatter.format_html(api_ref),
152            OutputFormat::Markdown => self.formatter.format_markdown(api_ref),
153            OutputFormat::Interactive => self.formatter.format_interactive(api_ref),
154            OutputFormat::OpenApi => self.formatter.format_openapi(api_ref),
155        }
156    }
157
158    /// Get the document formatter
159    pub fn formatter(&self) -> &DocumentFormatter {
160        &self.formatter
161    }
162
163    /// Get a mutable reference to the document formatter
164    pub fn formatter_mut(&mut self) -> &mut DocumentFormatter {
165        &mut self.formatter
166    }
167
168    /// Analyze crate structure and metadata
169    fn analyze_crate(&self, crate_name: &str) -> Result<CrateInfo> {
170        // In a real implementation, this would use syn/quote to parse the crate
171        Ok(CrateInfo {
172            name: crate_name.to_string(),
173            version: "0.1.0".to_string(),
174            description: format!("API reference for {}", crate_name),
175            modules: vec![
176                "core".to_string(),
177                "traits".to_string(),
178                "error".to_string(),
179                "utils".to_string(),
180            ],
181            dependencies: vec![
182                "serde".to_string(),
183                "ndarray".to_string(),
184                "thiserror".to_string(),
185            ],
186        })
187    }
188
189    /// Analyze source files directly
190    fn analyze_source_files(&self, _source_files: &[PathBuf]) -> Result<CrateInfo> {
191        // In a real implementation, this would parse the source files
192        Ok(CrateInfo {
193            name: "custom".to_string(),
194            version: "unknown".to_string(),
195            description: "Custom source file analysis".to_string(),
196            modules: Vec::new(),
197            dependencies: Vec::new(),
198        })
199    }
200
201    /// Extract code examples from documentation
202    fn extract_examples(&self, crate_info: &CrateInfo) -> Result<Vec<CodeExample>> {
203        // Generate examples based on crate content
204        let mut examples = Vec::new();
205
206        examples.push(CodeExample {
207            title: "Basic Usage".to_string(),
208            description: format!("Demonstrates basic usage of {}", crate_info.name),
209            code: format!(
210                r#"use {};
211
212fn main() {{
213    // Your code here
214    println!("Hello from {}!");
215}}"#,
216                crate_info.name, crate_info.name
217            ),
218            language: "rust".to_string(),
219            runnable: true,
220            expected_output: Some(format!("Hello from {}!", crate_info.name)),
221        });
222
223        examples.push(CodeExample {
224            title: "Advanced Example".to_string(),
225            description: "Shows advanced features and patterns".to_string(),
226            code: format!(
227                r#"use {}::{{traits::*, error::*}};
228
229fn advanced_example() -> Result<(), Box<dyn std::error::Error>> {{
230    // Advanced usage patterns
231    let result = process_data()?;
232    println!("Result: {{:?}}", result);
233    Ok(())
234}}
235
236fn process_data() -> Result<String, {}Error> {{
237    // Complex processing logic
238    Ok("Processed data".to_string())
239}}"#,
240                crate_info.name,
241                crate_info.name.replace('-', "").to_title_case()
242            ),
243            language: "rust".to_string(),
244            runnable: true,
245            expected_output: Some("Result: \"Processed data\"".to_string()),
246        });
247
248        Ok(examples)
249    }
250
251    /// Extract examples from source files
252    fn extract_examples_from_files(&self, _source_files: &[PathBuf]) -> Result<Vec<CodeExample>> {
253        // In a real implementation, this would parse doc comments from source files
254        Ok(vec![CodeExample {
255            title: "Source File Example".to_string(),
256            description: "Example extracted from source file documentation".to_string(),
257            code: "// Example code from source files".to_string(),
258            language: "rust".to_string(),
259            runnable: false,
260            expected_output: None,
261        }])
262    }
263
264    /// Generate metadata for the API reference
265    fn generate_metadata(&self, crate_info: &CrateInfo) -> Result<ApiMetadata> {
266        Ok(ApiMetadata {
267            generation_time: chrono::Utc::now().to_string(),
268            generator_version: env!("CARGO_PKG_VERSION").to_string(),
269            crate_version: crate_info.version.clone(),
270            rust_version: env!("CARGO_PKG_RUST_VERSION").to_string(),
271            config: self.config.clone(),
272        })
273    }
274}
275
276impl Default for ApiReferenceGenerator {
277    fn default() -> Self {
278        Self::new(GeneratorConfig::default())
279    }
280}
281
282// ================================================================================================
283// DOCUMENT FORMATTER
284// ================================================================================================
285
286/// Document formatter for converting API references to various output formats
287#[derive(Debug, Clone)]
288pub struct DocumentFormatter {
289    #[allow(dead_code)]
290    config: GeneratorConfig,
291    theme: ThemeConfig,
292    custom_templates: HashMap<String, String>,
293}
294
295impl DocumentFormatter {
296    /// Create a new document formatter
297    pub fn new(config: GeneratorConfig) -> Self {
298        Self {
299            theme: ThemeConfig::default(),
300            config,
301            custom_templates: HashMap::new(),
302        }
303    }
304
305    /// Create formatter with custom theme
306    pub fn with_theme(config: GeneratorConfig, theme: ThemeConfig) -> Self {
307        Self {
308            config,
309            theme,
310            custom_templates: HashMap::new(),
311        }
312    }
313
314    /// Format API reference as JSON
315    pub fn format_json(&self, api_ref: &ApiReference) -> Result<String> {
316        serde_json::to_string_pretty(api_ref)
317            .map_err(|e| SklearsError::InvalidInput(format!("JSON serialization failed: {}", e)))
318    }
319
320    /// Format API reference as HTML
321    pub fn format_html(&self, api_ref: &ApiReference) -> Result<String> {
322        let mut html = String::new();
323
324        // HTML header with theme styling
325        html.push_str("<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n");
326        html.push_str("    <meta charset=\"UTF-8\">\n");
327        html.push_str(
328            "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n",
329        );
330        html.push_str(&format!(
331            "    <title>API Reference - {}</title>\n",
332            api_ref.crate_name
333        ));
334        html.push_str("    <style>\n");
335        html.push_str(&self.generate_html_css()?);
336        html.push_str("    </style>\n");
337        html.push_str("</head>\n<body>\n");
338
339        // Navigation
340        html.push_str(&self.generate_navigation(api_ref)?);
341
342        // Main content
343        html.push_str("    <main class=\"content\">\n");
344        html.push_str(&format!(
345            "        <h1>API Reference for {}</h1>\n",
346            api_ref.crate_name
347        ));
348        html.push_str(&format!(
349            "        <p class=\"version\">Version: {}</p>\n",
350            api_ref.version
351        ));
352
353        // Table of contents
354        html.push_str(&self.generate_table_of_contents(api_ref)?);
355
356        // Traits section
357        if !api_ref.traits.is_empty() {
358            html.push_str(&self.format_traits_html(&api_ref.traits)?);
359        }
360
361        // Types section
362        if !api_ref.types.is_empty() {
363            html.push_str(&self.format_types_html(&api_ref.types)?);
364        }
365
366        // Examples section
367        if !api_ref.examples.is_empty() {
368            html.push_str(&self.format_examples_html(&api_ref.examples)?);
369        }
370
371        // Cross-references section
372        if !api_ref.cross_references.is_empty() {
373            html.push_str(&self.format_cross_refs_html(&api_ref.cross_references)?);
374        }
375
376        html.push_str("    </main>\n");
377
378        // Footer
379        html.push_str(&self.generate_footer(api_ref)?);
380
381        html.push_str("</body>\n</html>");
382        Ok(html)
383    }
384
385    /// Format API reference as Markdown
386    pub fn format_markdown(&self, api_ref: &ApiReference) -> Result<String> {
387        let mut md = String::new();
388
389        // Header
390        md.push_str(&format!("# API Reference - {}\n\n", api_ref.crate_name));
391        md.push_str(&format!("**Version:** {}\n\n", api_ref.version));
392        md.push_str(&format!(
393            "**Generated:** {}\n\n",
394            api_ref.metadata.generation_time
395        ));
396
397        // Table of contents
398        md.push_str("## Table of Contents\n\n");
399        if !api_ref.traits.is_empty() {
400            md.push_str("- [Traits](#traits)\n");
401        }
402        if !api_ref.types.is_empty() {
403            md.push_str("- [Types](#types)\n");
404        }
405        if !api_ref.examples.is_empty() {
406            md.push_str("- [Examples](#examples)\n");
407        }
408        if !api_ref.cross_references.is_empty() {
409            md.push_str("- [Cross References](#cross-references)\n");
410        }
411        md.push('\n');
412
413        // Traits section
414        if !api_ref.traits.is_empty() {
415            md.push_str(&self.format_traits_markdown(&api_ref.traits)?);
416        }
417
418        // Types section
419        if !api_ref.types.is_empty() {
420            md.push_str(&self.format_types_markdown(&api_ref.types)?);
421        }
422
423        // Examples section
424        if !api_ref.examples.is_empty() {
425            md.push_str(&self.format_examples_markdown(&api_ref.examples)?);
426        }
427
428        // Cross-references section
429        if !api_ref.cross_references.is_empty() {
430            md.push_str(&self.format_cross_refs_markdown(&api_ref.cross_references)?);
431        }
432
433        Ok(md)
434    }
435
436    /// Format API reference as interactive HTML
437    pub fn format_interactive(&self, api_ref: &ApiReference) -> Result<String> {
438        let mut html = String::new();
439
440        html.push_str("<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n");
441        html.push_str("    <meta charset=\"UTF-8\">\n");
442        html.push_str(
443            "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n",
444        );
445        html.push_str(&format!(
446            "    <title>Interactive API Reference - {}</title>\n",
447            api_ref.crate_name
448        ));
449
450        // External libraries for interactive features
451        html.push_str("    <script src=\"https://cdn.jsdelivr.net/npm/monaco-editor@latest/min/vs/loader.js\"></script>\n");
452        html.push_str("    <script src=\"https://unpkg.com/@webassembly/wasi-sdk@0.11.0/bin/wasm-ld\"></script>\n");
453
454        // CSS for interactive layout
455        html.push_str("    <style>\n");
456        html.push_str(&self.generate_interactive_css()?);
457        html.push_str("    </style>\n");
458        html.push_str("</head>\n<body>\n");
459
460        // Interactive header
461        html.push_str("    <header class=\"interactive-header\">\n");
462        html.push_str(&format!(
463            "        <h1>Interactive API Reference - {}</h1>\n",
464            api_ref.crate_name
465        ));
466        html.push_str("        <nav class=\"interactive-nav\">\n");
467        html.push_str("            <button id=\"run-code\" class=\"nav-btn\">Run Code</button>\n");
468        html.push_str("            <button id=\"reset-code\" class=\"nav-btn\">Reset</button>\n");
469        html.push_str("            <button id=\"share-code\" class=\"nav-btn\">Share</button>\n");
470        html.push_str("        </nav>\n");
471        html.push_str("    </header>\n");
472
473        // Main interactive area
474        html.push_str("    <main class=\"interactive-main\">\n");
475        html.push_str("        <div class=\"editor-panel\">\n");
476        html.push_str("            <h2>Code Editor</h2>\n");
477        html.push_str("            <div id=\"code-editor\"></div>\n");
478        html.push_str("        </div>\n");
479        html.push_str("        <div class=\"output-panel\">\n");
480        html.push_str("            <h2>Output</h2>\n");
481        html.push_str("            <div id=\"output-display\"></div>\n");
482        html.push_str("        </div>\n");
483        html.push_str("        <div class=\"api-panel\">\n");
484        html.push_str("            <h2>API Explorer</h2>\n");
485        html.push_str(&self.format_interactive_api_explorer(api_ref)?);
486        html.push_str("        </div>\n");
487        html.push_str("    </main>\n");
488
489        // Examples gallery
490        html.push_str("    <section class=\"examples-gallery\">\n");
491        html.push_str("        <h2>Examples</h2>\n");
492        html.push_str(&self.format_interactive_examples(&api_ref.examples)?);
493        html.push_str("    </section>\n");
494
495        // JavaScript for interactive functionality
496        html.push_str("    <script>\n");
497        html.push_str(&self.generate_interactive_js(api_ref)?);
498        html.push_str("    </script>\n");
499
500        html.push_str("</body>\n</html>");
501        Ok(html)
502    }
503
504    /// Format API reference as OpenAPI specification
505    pub fn format_openapi(&self, api_ref: &ApiReference) -> Result<String> {
506        let mut openapi = String::new();
507
508        openapi.push_str("openapi: 3.0.3\n");
509        openapi.push_str("info:\n");
510        openapi.push_str(&format!("  title: {} API\n", api_ref.crate_name));
511        openapi.push_str(&format!("  version: {}\n", api_ref.version));
512        openapi.push_str(&format!(
513            "  description: API specification for {}\n",
514            api_ref.crate_name
515        ));
516        openapi.push_str("servers:\n");
517        openapi.push_str("  - url: http://localhost:8080\n");
518        openapi.push_str("    description: Development server\n");
519
520        openapi.push_str("paths:\n");
521
522        // Generate paths from traits (treating them as endpoints)
523        for trait_info in &api_ref.traits {
524            for method in &trait_info.methods {
525                let path = format!("/{}/{}", trait_info.name.to_lowercase(), method.name);
526                openapi.push_str(&format!("  {}:\n", path));
527                openapi.push_str("    post:\n");
528                openapi.push_str(&format!("      summary: {}\n", method.description));
529                openapi.push_str(&format!(
530                    "      operationId: {}_{}\n",
531                    trait_info.name, method.name
532                ));
533                openapi.push_str("      responses:\n");
534                openapi.push_str("        '200':\n");
535                openapi.push_str("          description: Successful operation\n");
536                openapi.push_str("          content:\n");
537                openapi.push_str("            application/json:\n");
538                openapi.push_str("              schema:\n");
539                openapi.push_str("                type: object\n");
540            }
541        }
542
543        openapi.push_str("components:\n");
544        openapi.push_str("  schemas:\n");
545
546        // Generate schemas from types
547        for type_info in &api_ref.types {
548            openapi.push_str(&format!("    {}:\n", type_info.name));
549            openapi.push_str("      type: object\n");
550            if !type_info.fields.is_empty() {
551                openapi.push_str("      properties:\n");
552                for field in &type_info.fields {
553                    openapi.push_str(&format!("        {}:\n", field.name));
554                    openapi.push_str(&format!(
555                        "          type: {}\n",
556                        self.rust_type_to_openapi(&field.field_type)
557                    ));
558                    if !field.description.is_empty() {
559                        openapi
560                            .push_str(&format!("          description: {}\n", field.description));
561                    }
562                }
563            }
564        }
565
566        Ok(openapi)
567    }
568
569    /// Set custom theme
570    pub fn set_theme(&mut self, theme: ThemeConfig) {
571        self.theme = theme;
572    }
573
574    /// Add custom template
575    pub fn add_template(&mut self, name: String, template: String) {
576        self.custom_templates.insert(name, template);
577    }
578
579    /// Generate HTML CSS styles
580    fn generate_html_css(&self) -> Result<String> {
581        let mut css = String::new();
582
583        // Theme variables
584        css.push_str(&self.theme.to_css_variables());
585        css.push_str("\n\n");
586
587        // Base styles
588        css.push_str(
589            r#"
590        body {
591            font-family: var(--font-family);
592            background-color: var(--background-color);
593            color: var(--text-color);
594            line-height: 1.6;
595            margin: 0;
596            padding: 0;
597        }
598
599        .content {
600            max-width: 1200px;
601            margin: 0 auto;
602            padding: 2rem;
603        }
604
605        h1, h2, h3, h4, h5, h6 {
606            color: var(--primary-color);
607            margin-top: 2rem;
608            margin-bottom: 1rem;
609        }
610
611        .version {
612            color: var(--secondary-color);
613            font-weight: bold;
614        }
615
616        .toc {
617            background: var(--code-background);
618            padding: 1rem;
619            border-radius: 8px;
620            margin: 2rem 0;
621        }
622
623        .toc ul {
624            list-style-type: none;
625            padding-left: 1rem;
626        }
627
628        .toc a {
629            color: var(--primary-color);
630            text-decoration: none;
631        }
632
633        .toc a:hover {
634            text-decoration: underline;
635        }
636
637        .trait-section, .type-section, .example-section {
638            margin: 3rem 0;
639            padding: 2rem;
640            border: 1px solid #ddd;
641            border-radius: 8px;
642        }
643
644        .method-list {
645            margin-left: 1rem;
646        }
647
648        .method-item {
649            margin: 1rem 0;
650            padding: 1rem;
651            background: var(--code-background);
652            border-radius: 4px;
653        }
654
655        .method-signature {
656            font-family: var(--code-font-family);
657            background: var(--code-background);
658            color: var(--code-text-color);
659            padding: 0.5rem;
660            border-radius: 4px;
661            font-size: 0.9rem;
662        }
663
664        .code-example {
665            background: var(--code-background);
666            color: var(--code-text-color);
667            padding: 1rem;
668            border-radius: 4px;
669            overflow-x: auto;
670            margin: 1rem 0;
671        }
672
673        .code-example pre {
674            margin: 0;
675            font-family: var(--code-font-family);
676        }
677
678        .footer {
679            margin-top: 4rem;
680            padding: 2rem;
681            border-top: 1px solid #ddd;
682            text-align: center;
683            color: #666;
684        }
685
686        .nav {
687            background: var(--primary-color);
688            padding: 1rem;
689            margin-bottom: 2rem;
690        }
691
692        .nav a {
693            color: white;
694            text-decoration: none;
695            margin-right: 1rem;
696            padding: 0.5rem 1rem;
697            border-radius: 4px;
698            transition: background 0.2s;
699        }
700
701        .nav a:hover {
702            background: rgba(255, 255, 255, 0.2);
703        }
704        "#,
705        );
706
707        Ok(css)
708    }
709
710    /// Generate navigation HTML
711    fn generate_navigation(&self, api_ref: &ApiReference) -> Result<String> {
712        let mut nav = String::new();
713        nav.push_str("    <nav class=\"nav\">\n");
714        if !api_ref.traits.is_empty() {
715            nav.push_str("        <a href=\"#traits\">Traits</a>\n");
716        }
717        if !api_ref.types.is_empty() {
718            nav.push_str("        <a href=\"#types\">Types</a>\n");
719        }
720        if !api_ref.examples.is_empty() {
721            nav.push_str("        <a href=\"#examples\">Examples</a>\n");
722        }
723        nav.push_str("    </nav>\n");
724        Ok(nav)
725    }
726
727    /// Generate table of contents
728    fn generate_table_of_contents(&self, api_ref: &ApiReference) -> Result<String> {
729        let mut toc = String::new();
730        toc.push_str("        <div class=\"toc\">\n");
731        toc.push_str("            <h2>Table of Contents</h2>\n");
732        toc.push_str("            <ul>\n");
733
734        if !api_ref.traits.is_empty() {
735            toc.push_str("                <li><a href=\"#traits\">Traits</a>\n");
736            toc.push_str("                    <ul>\n");
737            for trait_info in &api_ref.traits {
738                toc.push_str(&format!(
739                    "                        <li><a href=\"#{}\">{}::{}</a></li>\n",
740                    trait_info.name.to_lowercase(),
741                    trait_info
742                        .path
743                        .split("::")
744                        .last()
745                        .unwrap_or(&trait_info.path),
746                    trait_info.name
747                ));
748            }
749            toc.push_str("                    </ul>\n");
750            toc.push_str("                </li>\n");
751        }
752
753        if !api_ref.types.is_empty() {
754            toc.push_str("                <li><a href=\"#types\">Types</a>\n");
755            toc.push_str("                    <ul>\n");
756            for type_info in &api_ref.types {
757                toc.push_str(&format!(
758                    "                        <li><a href=\"#{}\">{}::{}</a></li>\n",
759                    type_info.name.to_lowercase(),
760                    type_info.path.split("::").last().unwrap_or(&type_info.path),
761                    type_info.name
762                ));
763            }
764            toc.push_str("                    </ul>\n");
765            toc.push_str("                </li>\n");
766        }
767
768        if !api_ref.examples.is_empty() {
769            toc.push_str("                <li><a href=\"#examples\">Examples</a></li>\n");
770        }
771
772        toc.push_str("            </ul>\n");
773        toc.push_str("        </div>\n");
774        Ok(toc)
775    }
776
777    /// Format traits as HTML
778    fn format_traits_html(&self, traits: &[TraitInfo]) -> Result<String> {
779        let mut html = String::new();
780        html.push_str("        <section id=\"traits\">\n");
781        html.push_str("            <h2>Traits</h2>\n");
782
783        for trait_info in traits {
784            html.push_str(&format!(
785                "            <div class=\"trait-section\" id=\"{}\">\n",
786                trait_info.name.to_lowercase()
787            ));
788            html.push_str(&format!("                <h3>{}</h3>\n", trait_info.name));
789            html.push_str(&format!(
790                "                <p>{}</p>\n",
791                trait_info.description
792            ));
793            html.push_str(&format!(
794                "                <p><strong>Path:</strong> <code>{}</code></p>\n",
795                trait_info.path
796            ));
797
798            if !trait_info.generics.is_empty() {
799                html.push_str("                <p><strong>Generic Parameters:</strong> ");
800                html.push_str(&trait_info.generics.join(", "));
801                html.push_str("</p>\n");
802            }
803
804            if !trait_info.supertraits.is_empty() {
805                html.push_str("                <p><strong>Supertraits:</strong> ");
806                html.push_str(&trait_info.supertraits.join(", "));
807                html.push_str("</p>\n");
808            }
809
810            if !trait_info.methods.is_empty() {
811                html.push_str("                <h4>Methods</h4>\n");
812                html.push_str("                <div class=\"method-list\">\n");
813                for method in &trait_info.methods {
814                    html.push_str("                    <div class=\"method-item\">\n");
815                    html.push_str(&format!(
816                        "                        <h5>{}</h5>\n",
817                        method.name
818                    ));
819                    html.push_str(&format!(
820                        "                        <div class=\"method-signature\"><code>{}</code></div>\n",
821                        method.signature
822                    ));
823                    html.push_str(&format!(
824                        "                        <p>{}</p>\n",
825                        method.description
826                    ));
827                    if method.required {
828                        html.push_str("                        <p><em>Required method</em></p>\n");
829                    }
830                    html.push_str("                    </div>\n");
831                }
832                html.push_str("                </div>\n");
833            }
834
835            html.push_str("            </div>\n");
836        }
837
838        html.push_str("        </section>\n");
839        Ok(html)
840    }
841
842    /// Format types as HTML
843    fn format_types_html(&self, types: &[TypeInfo]) -> Result<String> {
844        let mut html = String::new();
845        html.push_str("        <section id=\"types\">\n");
846        html.push_str("            <h2>Types</h2>\n");
847
848        for type_info in types {
849            html.push_str(&format!(
850                "            <div class=\"type-section\" id=\"{}\">\n",
851                type_info.name.to_lowercase()
852            ));
853            html.push_str(&format!("                <h3>{}</h3>\n", type_info.name));
854            html.push_str(&format!(
855                "                <p>{}</p>\n",
856                type_info.description
857            ));
858            html.push_str(&format!(
859                "                <p><strong>Path:</strong> <code>{}</code></p>\n",
860                type_info.path
861            ));
862            html.push_str(&format!(
863                "                <p><strong>Kind:</strong> {:?}</p>\n",
864                type_info.kind
865            ));
866
867            if !type_info.fields.is_empty() {
868                html.push_str("                <h4>Fields</h4>\n");
869                html.push_str("                <ul>\n");
870                for field in &type_info.fields {
871                    html.push_str(&format!(
872                        "                    <li><strong>{}:</strong> <code>{}</code> - {}</li>\n",
873                        field.name, field.field_type, field.description
874                    ));
875                }
876                html.push_str("                </ul>\n");
877            }
878
879            if !type_info.trait_impls.is_empty() {
880                html.push_str("                <p><strong>Implemented Traits:</strong> ");
881                html.push_str(&type_info.trait_impls.join(", "));
882                html.push_str("</p>\n");
883            }
884
885            html.push_str("            </div>\n");
886        }
887
888        html.push_str("        </section>\n");
889        Ok(html)
890    }
891
892    /// Format examples as HTML
893    fn format_examples_html(&self, examples: &[CodeExample]) -> Result<String> {
894        let mut html = String::new();
895        html.push_str("        <section id=\"examples\">\n");
896        html.push_str("            <h2>Examples</h2>\n");
897
898        for example in examples {
899            html.push_str("            <div class=\"example-section\">\n");
900            html.push_str(&format!("                <h3>{}</h3>\n", example.title));
901            html.push_str(&format!("                <p>{}</p>\n", example.description));
902            html.push_str(&format!(
903                "                <div class=\"code-example\"><pre><code class=\"{}\">{}</code></pre></div>\n",
904                example.language, example.code
905            ));
906            if let Some(output) = &example.expected_output {
907                html.push_str("                <p><strong>Expected Output:</strong></p>\n");
908                html.push_str(&format!(
909                    "                <div class=\"code-example\"><pre>{}</pre></div>\n",
910                    output
911                ));
912            }
913            html.push_str("            </div>\n");
914        }
915
916        html.push_str("        </section>\n");
917        Ok(html)
918    }
919
920    /// Format cross-references as HTML
921    fn format_cross_refs_html(&self, cross_refs: &HashMap<String, Vec<String>>) -> Result<String> {
922        let mut html = String::new();
923        html.push_str("        <section id=\"cross-references\">\n");
924        html.push_str("            <h2>Cross References</h2>\n");
925
926        for (item, refs) in cross_refs {
927            if !refs.is_empty() {
928                html.push_str(&format!("            <h3>{}</h3>\n", item));
929                html.push_str("            <ul>\n");
930                for ref_item in refs {
931                    html.push_str(&format!("                <li>{}</li>\n", ref_item));
932                }
933                html.push_str("            </ul>\n");
934            }
935        }
936
937        html.push_str("        </section>\n");
938        Ok(html)
939    }
940
941    /// Format traits as Markdown
942    fn format_traits_markdown(&self, traits: &[TraitInfo]) -> Result<String> {
943        let mut md = String::new();
944        md.push_str("## Traits\n\n");
945
946        for trait_info in traits {
947            md.push_str(&format!("### {}\n\n", trait_info.name));
948            md.push_str(&format!("{}\n\n", trait_info.description));
949            md.push_str(&format!("**Path:** `{}`\n\n", trait_info.path));
950
951            if !trait_info.generics.is_empty() {
952                md.push_str(&format!(
953                    "**Generic Parameters:** {}\n\n",
954                    trait_info.generics.join(", ")
955                ));
956            }
957
958            if !trait_info.supertraits.is_empty() {
959                md.push_str(&format!(
960                    "**Supertraits:** {}\n\n",
961                    trait_info.supertraits.join(", ")
962                ));
963            }
964
965            if !trait_info.methods.is_empty() {
966                md.push_str("#### Methods\n\n");
967                for method in &trait_info.methods {
968                    md.push_str(&format!("##### {}\n\n", method.name));
969                    md.push_str(&format!("```rust\n{}\n```\n\n", method.signature));
970                    md.push_str(&format!("{}\n\n", method.description));
971                    if method.required {
972                        md.push_str("*Required method*\n\n");
973                    }
974                }
975            }
976        }
977
978        Ok(md)
979    }
980
981    /// Format types as Markdown
982    fn format_types_markdown(&self, types: &[TypeInfo]) -> Result<String> {
983        let mut md = String::new();
984        md.push_str("## Types\n\n");
985
986        for type_info in types {
987            md.push_str(&format!("### {}\n\n", type_info.name));
988            md.push_str(&format!("{}\n\n", type_info.description));
989            md.push_str(&format!("**Path:** `{}`\n\n", type_info.path));
990            md.push_str(&format!("**Kind:** {:?}\n\n", type_info.kind));
991
992            if !type_info.fields.is_empty() {
993                md.push_str("#### Fields\n\n");
994                for field in &type_info.fields {
995                    md.push_str(&format!(
996                        "- **{}:** `{}` - {}\n",
997                        field.name, field.field_type, field.description
998                    ));
999                }
1000                md.push('\n');
1001            }
1002
1003            if !type_info.trait_impls.is_empty() {
1004                md.push_str(&format!(
1005                    "**Implemented Traits:** {}\n\n",
1006                    type_info.trait_impls.join(", ")
1007                ));
1008            }
1009        }
1010
1011        Ok(md)
1012    }
1013
1014    /// Format examples as Markdown
1015    fn format_examples_markdown(&self, examples: &[CodeExample]) -> Result<String> {
1016        let mut md = String::new();
1017        md.push_str("## Examples\n\n");
1018
1019        for example in examples {
1020            md.push_str(&format!("### {}\n\n", example.title));
1021            md.push_str(&format!("{}\n\n", example.description));
1022            md.push_str(&format!(
1023                "```{}\n{}\n```\n\n",
1024                example.language, example.code
1025            ));
1026            if let Some(output) = &example.expected_output {
1027                md.push_str("**Expected Output:**\n\n");
1028                md.push_str(&format!("```\n{}\n```\n\n", output));
1029            }
1030        }
1031
1032        Ok(md)
1033    }
1034
1035    /// Format cross-references as Markdown
1036    fn format_cross_refs_markdown(
1037        &self,
1038        cross_refs: &HashMap<String, Vec<String>>,
1039    ) -> Result<String> {
1040        let mut md = String::new();
1041        md.push_str("## Cross References\n\n");
1042
1043        for (item, refs) in cross_refs {
1044            if !refs.is_empty() {
1045                md.push_str(&format!("### {}\n\n", item));
1046                for ref_item in refs {
1047                    md.push_str(&format!("- {}\n", ref_item));
1048                }
1049                md.push('\n');
1050            }
1051        }
1052
1053        Ok(md)
1054    }
1055
1056    /// Generate footer HTML
1057    fn generate_footer(&self, api_ref: &ApiReference) -> Result<String> {
1058        Ok(format!(
1059            r#"    <footer class="footer">
1060        <p>Generated on {} by sklears-core API generator v{}</p>
1061        <p>Rust version: {}</p>
1062    </footer>"#,
1063            api_ref.metadata.generation_time,
1064            api_ref.metadata.generator_version,
1065            api_ref.metadata.rust_version
1066        ))
1067    }
1068
1069    /// Generate interactive CSS
1070    fn generate_interactive_css(&self) -> Result<String> {
1071        Ok(r#"
1072        .interactive-header {
1073            background: var(--primary-color);
1074            color: white;
1075            padding: 1rem;
1076            display: flex;
1077            justify-content: space-between;
1078            align-items: center;
1079        }
1080
1081        .interactive-nav {
1082            display: flex;
1083            gap: 1rem;
1084        }
1085
1086        .nav-btn {
1087            background: rgba(255, 255, 255, 0.2);
1088            color: white;
1089            border: none;
1090            padding: 0.5rem 1rem;
1091            border-radius: 4px;
1092            cursor: pointer;
1093            transition: background 0.2s;
1094        }
1095
1096        .nav-btn:hover {
1097            background: rgba(255, 255, 255, 0.3);
1098        }
1099
1100        .interactive-main {
1101            display: grid;
1102            grid-template-columns: 1fr 1fr 300px;
1103            gap: 1rem;
1104            padding: 1rem;
1105            height: 70vh;
1106        }
1107
1108        .editor-panel, .output-panel, .api-panel {
1109            border: 1px solid #ddd;
1110            border-radius: 8px;
1111            padding: 1rem;
1112            overflow: hidden;
1113        }
1114
1115        #code-editor {
1116            height: 90%;
1117            border: 1px solid #ddd;
1118            border-radius: 4px;
1119        }
1120
1121        #output-display {
1122            height: 90%;
1123            background: var(--code-background);
1124            color: var(--code-text-color);
1125            padding: 1rem;
1126            border-radius: 4px;
1127            overflow-y: auto;
1128            font-family: var(--code-font-family);
1129        }
1130
1131        .api-panel {
1132            overflow-y: auto;
1133        }
1134
1135        .examples-gallery {
1136            padding: 1rem;
1137            border-top: 1px solid #ddd;
1138        }
1139
1140        .example-card {
1141            background: var(--code-background);
1142            padding: 1rem;
1143            margin: 0.5rem;
1144            border-radius: 4px;
1145            cursor: pointer;
1146            transition: transform 0.2s;
1147        }
1148
1149        .example-card:hover {
1150            transform: translateY(-2px);
1151        }
1152        "#
1153        .to_string())
1154    }
1155
1156    /// Format interactive API explorer
1157    fn format_interactive_api_explorer(&self, api_ref: &ApiReference) -> Result<String> {
1158        let mut html = String::new();
1159
1160        html.push_str("            <div class=\"api-search\">\n");
1161        html.push_str("                <input type=\"text\" placeholder=\"Search API...\" id=\"api-search-input\">\n");
1162        html.push_str("            </div>\n");
1163
1164        if !api_ref.traits.is_empty() {
1165            html.push_str("            <div class=\"api-section\">\n");
1166            html.push_str("                <h3>Traits</h3>\n");
1167            for trait_info in &api_ref.traits {
1168                html.push_str(&format!(
1169                    "                <div class=\"api-item\" data-type=\"trait\" onclick=\"insertCode('{}')\">{}</div>\n",
1170                    trait_info.name, trait_info.name
1171                ));
1172            }
1173            html.push_str("            </div>\n");
1174        }
1175
1176        if !api_ref.types.is_empty() {
1177            html.push_str("            <div class=\"api-section\">\n");
1178            html.push_str("                <h3>Types</h3>\n");
1179            for type_info in &api_ref.types {
1180                html.push_str(&format!(
1181                    "                <div class=\"api-item\" data-type=\"type\" onclick=\"insertCode('{}')\">{}</div>\n",
1182                    type_info.name, type_info.name
1183                ));
1184            }
1185            html.push_str("            </div>\n");
1186        }
1187
1188        Ok(html)
1189    }
1190
1191    /// Format interactive examples
1192    fn format_interactive_examples(&self, examples: &[CodeExample]) -> Result<String> {
1193        let mut html = String::new();
1194
1195        html.push_str("        <div class=\"examples-grid\">\n");
1196        for (i, example) in examples.iter().enumerate() {
1197            html.push_str(&format!(
1198                r#"            <div class="example-card" onclick="loadExample({})" title="{}">
1199                <h4>{}</h4>
1200                <p>{}</p>
1201            </div>"#,
1202                i, example.description, example.title, example.description
1203            ));
1204        }
1205        html.push_str("        </div>\n");
1206
1207        Ok(html)
1208    }
1209
1210    /// Generate interactive JavaScript
1211    fn generate_interactive_js(&self, api_ref: &ApiReference) -> Result<String> {
1212        let mut js = String::new();
1213
1214        // Examples data
1215        js.push_str("        const examples = [\n");
1216        for example in &api_ref.examples {
1217            js.push_str(&format!(
1218                "            {{ title: '{}', code: `{}` }},\n",
1219                example.title.replace('\'', "\\'"),
1220                example.code.replace('`', "\\`")
1221            ));
1222        }
1223        js.push_str("        ];\n\n");
1224
1225        // Interactive functionality
1226        js.push_str(r#"
1227        let editor;
1228
1229        // Initialize Monaco Editor
1230        require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@latest/min/vs' }});
1231        require(['vs/editor/editor.main'], function () {
1232            editor = monaco.editor.create(document.getElementById('code-editor'), {
1233                value: examples[0] ? examples[0].code : 'fn main() {\n    println!("Hello, sklears!");\n}',
1234                language: 'rust',
1235                theme: 'vs-dark',
1236                automaticLayout: true
1237            });
1238        });
1239
1240        function runCode() {
1241            const code = editor.getValue();
1242            const output = document.getElementById('output-display');
1243            output.innerHTML = 'Running code...\n\n' + code;
1244        }
1245
1246        function resetCode() {
1247            if (examples[0]) {
1248                editor.setValue(examples[0].code);
1249            }
1250            document.getElementById('output-display').innerHTML = 'Output will appear here...';
1251        }
1252
1253        function shareCode() {
1254            const code = editor.getValue();
1255            const encoded = btoa(code);
1256            const url = window.location.origin + window.location.pathname + '?code=' + encoded;
1257            navigator.clipboard.writeText(url);
1258            alert('Shareable link copied to clipboard!');
1259        }
1260
1261        function loadExample(index) {
1262            if (examples[index]) {
1263                editor.setValue(examples[index].code);
1264                document.getElementById('output-display').innerHTML = 'Example loaded: ' + examples[index].title;
1265            }
1266        }
1267
1268        function insertCode(apiItem) {
1269            const currentCode = editor.getValue();
1270            const newCode = currentCode + '\n// Using ' + apiItem + '\n';
1271            editor.setValue(newCode);
1272        }
1273
1274        // Event listeners
1275        document.getElementById('run-code').addEventListener('click', runCode);
1276        document.getElementById('reset-code').addEventListener('click', resetCode);
1277        document.getElementById('share-code').addEventListener('click', shareCode);
1278
1279        // API search functionality
1280        document.getElementById('api-search-input').addEventListener('input', function(e) {
1281            const query = e.target.value.toLowerCase();
1282            const items = document.querySelectorAll('.api-item');
1283            items.forEach(item => {
1284                if (item.textContent.toLowerCase().includes(query)) {
1285                    item.style.display = 'block';
1286                } else {
1287                    item.style.display = 'none';
1288                }
1289            });
1290        });
1291        "#);
1292
1293        Ok(js)
1294    }
1295
1296    /// Convert Rust type to OpenAPI type
1297    fn rust_type_to_openapi(&self, rust_type: &str) -> &'static str {
1298        match rust_type {
1299            "String" | "&str" => "string",
1300            "i32" | "i64" | "u32" | "u64" | "usize" | "isize" => "integer",
1301            "f32" | "f64" => "number",
1302            "bool" => "boolean",
1303            _ => "object",
1304        }
1305    }
1306}
1307
1308impl Default for DocumentFormatter {
1309    fn default() -> Self {
1310        Self::new(GeneratorConfig::default())
1311    }
1312}
1313
1314// ================================================================================================
1315// STRING UTILITIES
1316// ================================================================================================
1317
1318/// String extension trait for additional formatting utilities
1319trait StringExt {
1320    fn to_title_case(&self) -> String;
1321}
1322
1323impl StringExt for str {
1324    fn to_title_case(&self) -> String {
1325        self.chars()
1326            .enumerate()
1327            .map(|(i, c)| {
1328                if i == 0 || self.chars().nth(i - 1).unwrap_or(' ').is_whitespace() {
1329                    c.to_uppercase().collect::<String>()
1330                } else {
1331                    c.to_lowercase().collect::<String>()
1332                }
1333            })
1334            .collect()
1335    }
1336}
1337
1338// Mock chrono for compilation
1339mod chrono {
1340    pub struct DateTime<T>(std::marker::PhantomData<T>);
1341    pub struct Utc;
1342
1343    impl Utc {
1344        pub fn now() -> DateTime<Utc> {
1345            DateTime(std::marker::PhantomData)
1346        }
1347    }
1348
1349    impl<T> std::fmt::Display for DateTime<T> {
1350        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1351            write!(f, "2024-01-01T00:00:00Z")
1352        }
1353    }
1354}
1355
1356// ================================================================================================
1357// TESTS
1358// ================================================================================================
1359
1360#[allow(non_snake_case)]
1361#[cfg(test)]
1362mod tests {
1363    use super::*;
1364    use crate::api_data_structures::{FieldInfo, MethodInfo, TypeKind, Visibility};
1365
1366    #[test]
1367    fn test_document_formatter_json() {
1368        let formatter = DocumentFormatter::new(GeneratorConfig::new());
1369        let api_ref = create_test_api_reference();
1370
1371        let json = formatter.format_json(&api_ref).unwrap();
1372        assert!(json.contains("test-crate"));
1373        assert!(json.contains("traits"));
1374    }
1375
1376    #[test]
1377    fn test_document_formatter_html() {
1378        let formatter = DocumentFormatter::new(GeneratorConfig::new());
1379        let api_ref = create_test_api_reference();
1380
1381        let html = formatter.format_html(&api_ref).unwrap();
1382        assert!(html.contains("<html"));
1383        assert!(html.contains("API Reference for test-crate"));
1384        assert!(html.contains("</html>"));
1385    }
1386
1387    #[test]
1388    fn test_document_formatter_markdown() {
1389        let formatter = DocumentFormatter::new(GeneratorConfig::new());
1390        let api_ref = create_test_api_reference();
1391
1392        let md = formatter.format_markdown(&api_ref).unwrap();
1393        assert!(md.contains("# API Reference - test-crate"));
1394        assert!(md.contains("## Table of Contents"));
1395    }
1396
1397    #[test]
1398    fn test_document_formatter_interactive() {
1399        let formatter = DocumentFormatter::new(GeneratorConfig::new());
1400        let api_ref = create_test_api_reference();
1401
1402        let interactive = formatter.format_interactive(&api_ref).unwrap();
1403        assert!(interactive.contains("Interactive API Reference"));
1404        assert!(interactive.contains("code-editor"));
1405        assert!(interactive.contains("monaco-editor"));
1406    }
1407
1408    #[test]
1409    fn test_document_formatter_openapi() {
1410        let formatter = DocumentFormatter::new(GeneratorConfig::new());
1411        let api_ref = create_test_api_reference();
1412
1413        let openapi = formatter.format_openapi(&api_ref).unwrap();
1414        assert!(openapi.contains("openapi: 3.0.3"));
1415        assert!(openapi.contains("test-crate API"));
1416    }
1417
1418    #[test]
1419    fn test_api_reference_generator() {
1420        let config = GeneratorConfig::new();
1421        let mut generator = ApiReferenceGenerator::new(config);
1422
1423        let api_ref = generator.generate_from_crate("test-crate").unwrap();
1424        assert_eq!(api_ref.crate_name, "test-crate");
1425        assert!(!api_ref.traits.is_empty());
1426        assert!(!api_ref.examples.is_empty());
1427    }
1428
1429    #[test]
1430    fn test_format_output() {
1431        let config = GeneratorConfig::new().with_output_format(OutputFormat::Html);
1432        let generator = ApiReferenceGenerator::new(config);
1433        let api_ref = create_test_api_reference();
1434
1435        let output = generator.format_output(&api_ref).unwrap();
1436        assert!(output.contains("<html"));
1437    }
1438
1439    #[test]
1440    fn test_format_as() {
1441        let generator = ApiReferenceGenerator::new(GeneratorConfig::new());
1442        let api_ref = create_test_api_reference();
1443
1444        let json = generator.format_as(&api_ref, OutputFormat::Json).unwrap();
1445        assert!(json.contains("test-crate"));
1446
1447        let html = generator.format_as(&api_ref, OutputFormat::Html).unwrap();
1448        assert!(html.contains("<html"));
1449
1450        let md = generator
1451            .format_as(&api_ref, OutputFormat::Markdown)
1452            .unwrap();
1453        assert!(md.contains("# API Reference"));
1454    }
1455
1456    #[test]
1457    fn test_string_ext() {
1458        assert_eq!("hello world".to_title_case(), "Hello World");
1459        assert_eq!("HELLO WORLD".to_title_case(), "Hello World");
1460        assert_eq!("helloWorld".to_title_case(), "Helloworld");
1461    }
1462
1463    fn create_test_api_reference() -> ApiReference {
1464        ApiReference {
1465            crate_name: "test-crate".to_string(),
1466            version: "1.0.0".to_string(),
1467            traits: vec![TraitInfo {
1468                name: "TestTrait".to_string(),
1469                description: "A test trait".to_string(),
1470                path: "test::TestTrait".to_string(),
1471                generics: Vec::new(),
1472                associated_types: Vec::new(),
1473                methods: vec![MethodInfo {
1474                    name: "test_method".to_string(),
1475                    signature: "fn test_method(&self)".to_string(),
1476                    description: "A test method".to_string(),
1477                    parameters: Vec::new(),
1478                    return_type: "()".to_string(),
1479                    required: true,
1480                }],
1481                supertraits: Vec::new(),
1482                implementations: Vec::new(),
1483            }],
1484            types: vec![TypeInfo {
1485                name: "TestType".to_string(),
1486                description: "A test type".to_string(),
1487                path: "test::TestType".to_string(),
1488                kind: TypeKind::Struct,
1489                generics: Vec::new(),
1490                fields: vec![FieldInfo {
1491                    name: "field".to_string(),
1492                    field_type: "String".to_string(),
1493                    description: "A test field".to_string(),
1494                    visibility: Visibility::Public,
1495                }],
1496                trait_impls: Vec::new(),
1497            }],
1498            examples: vec![CodeExample {
1499                title: "Test Example".to_string(),
1500                description: "A test example".to_string(),
1501                code: "fn main() { println!(\"Hello, test!\"); }".to_string(),
1502                language: "rust".to_string(),
1503                runnable: true,
1504                expected_output: Some("Hello, test!".to_string()),
1505            }],
1506            cross_references: HashMap::new(),
1507            metadata: ApiMetadata::default(),
1508        }
1509    }
1510}