cargo_docs_md/generator/
capture.rs

1//! In-memory markdown capture for testing.
2//!
3//! This module provides [`MarkdownCapture`] for capturing generated markdown
4//! in memory instead of writing to disk, enabling snapshot testing.
5
6use std::collections::HashMap;
7
8/// Captures generated markdown in memory for testing.
9///
10/// Instead of writing files to disk, this struct stores all generated
11/// markdown content in a `HashMap`, keyed by relative file path. This
12/// enables snapshot testing and verification of output without filesystem
13/// side effects.
14#[derive(Debug, Default)]
15pub struct MarkdownCapture {
16    /// Maps file paths (relative to output directory) to their generated content.
17    files: HashMap<String, String>,
18}
19
20impl MarkdownCapture {
21    /// Create a new empty capture.
22    #[must_use]
23    pub fn new() -> Self {
24        Self {
25            files: HashMap::new(),
26        }
27    }
28
29    /// Add a file to the capture.
30    ///
31    /// # Arguments
32    /// * `path` - Relative path of the file (e.g., "index.md" or "span/index.md")
33    /// * `content` - The markdown content for this file
34    pub fn insert(&mut self, path: String, content: String) {
35        self.files.insert(path, content);
36    }
37
38    /// Get the content of a specific file.
39    #[must_use]
40    pub fn get(&self, path: &str) -> Option<&String> {
41        self.files.get(path)
42    }
43
44    /// Get all file paths in sorted order.
45    #[must_use]
46    pub fn paths(&self) -> Vec<&String> {
47        let mut paths: Vec<_> = self.files.keys().collect();
48        paths.sort();
49        paths
50    }
51
52    /// Get the number of captured files.
53    #[must_use]
54    pub fn len(&self) -> usize {
55        self.files.len()
56    }
57
58    /// Check if the capture is empty.
59    #[must_use]
60    pub fn is_empty(&self) -> bool {
61        self.files.is_empty()
62    }
63
64    /// Convert all captured files to a single string for snapshot testing.
65    ///
66    /// Files are sorted by path and separated with clear headers.
67    #[must_use]
68    pub fn to_snapshot_string(&self) -> String {
69        use std::fmt::Write;
70
71        let mut result = String::new();
72        let mut paths: Vec<_> = self.files.keys().collect();
73        paths.sort();
74
75        for path in paths {
76            _ = writeln!(result, "=== {path} ===");
77            _ = write!(result, "{}", &self.files[path]);
78
79            if !self.files[path].ends_with('\n') {
80                result.push('\n');
81            }
82
83            result.push('\n');
84        }
85
86        result
87    }
88
89    /// Consume self and return the underlying `HashMap`.
90    #[must_use]
91    pub fn into_inner(self) -> HashMap<String, String> {
92        self.files
93    }
94}