markdown_code_runner/
standardize.rs

1//! Code fence standardization utilities.
2
3use once_cell::sync::Lazy;
4use regex::Regex;
5
6/// Pattern to match code fence with markdown-code-runner and optional options.
7static STANDARDIZE_PATTERN: Lazy<Regex> =
8    Lazy::new(|| Regex::new(r"^(```\w+)\s+markdown-code-runner(?:\s+\S+=\S+)*\s*$").unwrap());
9
10/// Strip markdown-code-runner modifiers from all code fence language identifiers.
11///
12/// This is useful for making markdown files compatible with standard markdown
13/// processors like mkdocs and pandoc, which don't understand the
14/// `python markdown-code-runner` syntax.
15///
16/// # Examples
17///
18/// ```
19/// use markdown_code_runner::standardize::standardize_code_fences;
20///
21/// let content = "```python markdown-code-runner\nprint(\"hello\")\n```";
22/// let result = standardize_code_fences(content);
23/// assert_eq!(result, "```python\nprint(\"hello\")\n```");
24/// ```
25pub fn standardize_code_fences(content: &str) -> String {
26    let mut result = String::new();
27    for line in content.lines() {
28        if let Some(caps) = STANDARDIZE_PATTERN.captures(line) {
29            result.push_str(&caps[1]);
30        } else {
31            result.push_str(line);
32        }
33        result.push('\n');
34    }
35    // Remove the trailing newline we added if the original content didn't have one
36    if !content.ends_with('\n') && result.ends_with('\n') {
37        result.pop();
38    }
39    result
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn test_standardize_basic() {
48        let content = "```python markdown-code-runner\nprint('hello')\n```";
49        let expected = "```python\nprint('hello')\n```";
50        assert_eq!(standardize_code_fences(content), expected);
51    }
52
53    #[test]
54    fn test_standardize_with_options() {
55        let content = "```python markdown-code-runner filename=test.py debug=true\ncode here\n```";
56        let expected = "```python\ncode here\n```";
57        assert_eq!(standardize_code_fences(content), expected);
58    }
59
60    #[test]
61    fn test_standardize_multiple() {
62        let content = r#"```python markdown-code-runner
63first
64```
65text
66```bash markdown-code-runner
67second
68```
69more text
70```rust markdown-code-runner filename=test.rs
71third
72```"#;
73        let expected = r#"```python
74first
75```
76text
77```bash
78second
79```
80more text
81```rust
82third
83```"#;
84        assert_eq!(standardize_code_fences(content), expected);
85    }
86
87    #[test]
88    fn test_standardize_preserves_text_references() {
89        let content =
90            "Using `markdown-code-runner` is easy.\n```python markdown-code-runner\ncode\n```";
91        let expected = "Using `markdown-code-runner` is easy.\n```python\ncode\n```";
92        assert_eq!(standardize_code_fences(content), expected);
93    }
94
95    #[test]
96    fn test_standardize_leaves_normal_fences() {
97        let content = "```python\ncode\n```\n```javascript\nmore\n```";
98        assert_eq!(standardize_code_fences(content), content);
99    }
100}