ys1r/
markdown.rs

1/// Extracts the first fenced Markdown code block from the input string.
2///
3/// This function looks for a triple-backtick (` ``` `) fenced code block and
4/// returns the contents inside it. If a language identifier is present
5/// (e.g. ```rust), it is skipped automatically.
6///
7/// # Arguments
8///
9/// * `input` - The input string that may contain a fenced code block.
10///
11/// # Returns
12///
13/// * `Some(String)` containing the inner contents of the first code block
14/// * `None` if no fenced code block is found
15pub fn extract_code_block(input: &str) -> Option<String> {
16    let start = input.find("```")?;
17    let after_start = &input[start + 3..];
18
19    // Skip language identifier if present by advancing to the first newline
20    let content_start = after_start.find('\n').map_or(0, |pos| pos + 1);
21    let rest = &after_start[content_start..];
22
23    let end = rest.find("```")?;
24    Some(rest[..end].to_string())
25}
26
27/// Returns the contents of the first fenced Markdown code block if present,
28/// otherwise returns the original input unchanged.
29///
30/// This is a convenience wrapper around [`extract_code_block`] that guarantees
31/// a `String` result, making it useful when a fallback value is required.
32///
33/// # Arguments
34///
35/// * `input` - The input string that may contain a fenced code block.
36///
37/// # Returns
38///
39/// * The extracted code block contents if found
40/// * Otherwise, the original input converted to a `String`
41pub fn extract_code_block_or_original(input: &str) -> String {
42    extract_code_block(input).unwrap_or_else(|| input.to_string())
43}
44
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn extracts_simple_code_block() {
52        let input = "```\nhello\n```";
53        let result = extract_code_block(input);
54        assert_eq!(result, Some("hello\n".to_string()));
55    }
56
57    #[test]
58    fn extracts_code_block_with_language() {
59        let input = "```rust\nlet x = 42;\n```";
60        let result = extract_code_block(input);
61        assert_eq!(result, Some("let x = 42;\n".to_string()));
62    }
63
64    #[test]
65    fn extracts_first_code_block_only() {
66        let input = "```\nfirst\n```\n```\nsecond\n```";
67        let result = extract_code_block(input);
68        assert_eq!(result, Some("first\n".to_string()));
69    }
70
71    #[test]
72    fn returns_none_when_no_code_block() {
73        let input = "no code block here";
74        let result = extract_code_block(input);
75        assert_eq!(result, None);
76    }
77
78    #[test]
79    fn returns_original_when_no_code_block() {
80        let input = "no code block here";
81        let result = extract_code_block_or_original(input);
82        assert_eq!(result, input.to_string());
83    }
84
85    #[test]
86    fn returns_extracted_code_when_present() {
87        let input = "text before\n```\ncode\n```\ntext after";
88        let result = extract_code_block_or_original(input);
89        assert_eq!(result, "code\n".to_string());
90    }
91
92    #[test]
93    fn handles_unclosed_code_block() {
94        let input = "```\nunclosed";
95        let result = extract_code_block(input);
96        assert_eq!(result, None);
97    }
98}