batch_mode_json/
extract_json_from_possible_backticks_block.rs

1// ---------------- [ File: batch-mode-json/src/extract_json_from_possible_backticks_block.rs ]
2crate::ix!();
3
4/// Extracts JSON from a content string by removing surrounding ```json\n and \n``` markers if present,
5/// and trims any leading or trailing whitespace from the input.
6///
7/// # Arguments
8/// * `content` - The content string to process.
9///
10/// # Returns
11/// * `&str` - The processed content string with markers and extra whitespace removed,
12///   or the original trimmed content if markers are absent.
13pub fn extract_json_from_possible_backticks_block(content: &str) -> &str {
14    trace!("Entering extract_json_from_possible_backticks_block");
15    let trimmed_content = content.trim();
16    debug!("Trimmed content: '{}'", trimmed_content);
17
18    if trimmed_content.starts_with("```json") {
19        debug!("Detected start marker '```json'");
20        let json_start = 7; // Skip past "```json"
21        let remaining_content = &trimmed_content[json_start..];
22        debug!("Remaining after start marker: '{}'", remaining_content);
23
24        // Try to find the end marker
25        if let Some(end_marker_pos) = remaining_content.rfind("```") {
26            debug!("Detected end marker at position: {}", end_marker_pos);
27            let json_content = &remaining_content[..end_marker_pos].trim();
28            debug!("Extracted potential JSON content: '{}'", json_content);
29
30            if json_content.is_empty() {
31                debug!("JSON content is empty after trimming");
32                return "";
33            }
34
35            let final_content = json_content
36                .trim_start_matches('\n')
37                .trim_end_matches('\n')
38                .trim();
39            trace!("Final extracted JSON content with markers: '{}'", final_content);
40            return final_content;
41        }
42
43        // No closing backticks, treat everything after "```json" as content
44        warn!("No end marker found. Returning everything after '```json'.");
45        let final_content = remaining_content
46            .trim_start_matches('\n')
47            .trim_end_matches('\n')
48            .trim();
49        trace!("Final extracted JSON content without closing marker: '{}'", final_content);
50        return final_content;
51    }
52
53    // Not a JSON block, return the entire trimmed content
54    trace!("No JSON block found; returning trimmed content: '{}'", trimmed_content);
55    trimmed_content
56}
57
58#[cfg(test)]
59mod extract_json_from_possible_backticks_block_tests {
60    use super::*;
61
62    #[traced_test]
63    fn test_extract_json_with_markers() {
64        let content = "```json\n{\"key\": \"value\"}\n```";
65        let result = extract_json_from_possible_backticks_block(content);
66        pretty_assert_eq!(result, "{\"key\": \"value\"}");
67    }
68
69    #[traced_test]
70    fn test_extract_json_with_whitespace_and_markers() {
71        let content = "   \n```json\n{\"key\": \"value\"}\n```\n   ";
72        let result = extract_json_from_possible_backticks_block(content);
73        pretty_assert_eq!(result, "{\"key\": \"value\"}");
74    }
75
76    #[traced_test]
77    fn test_extract_json_no_markers() {
78        let content = "   {\"key\": \"value\"}   ";
79        let result = extract_json_from_possible_backticks_block(content);
80        pretty_assert_eq!(result, "{\"key\": \"value\"}");
81    }
82
83    #[traced_test]
84    fn test_extract_json_incomplete_markers() {
85        let content = "```json{\"key\": \"value\"}";
86        let result = extract_json_from_possible_backticks_block(content);
87        pretty_assert_eq!(result, "{\"key\": \"value\"}");
88    }
89
90    #[traced_test]
91    fn test_extract_json_trailing_newline() {
92        let content = "```json\n{\"key\": \"value\"}\n\n```";
93        let result = extract_json_from_possible_backticks_block(content);
94        pretty_assert_eq!(result, "{\"key\": \"value\"}");
95    }
96
97    #[traced_test]
98    fn test_extract_json_empty_string() {
99        let content = "   ";
100        let result = extract_json_from_possible_backticks_block(content);
101        pretty_assert_eq!(result, "");
102    }
103
104    #[traced_test]
105    fn test_extract_json_no_json_after_marker() {
106        let content = "```json```";
107        let result = extract_json_from_possible_backticks_block(content);
108        pretty_assert_eq!(result, "");
109    }
110
111    #[traced_test]
112    fn test_extract_json_with_crlf_line_endings() {
113        // Simulate Windows-style line endings
114        let content = "```json\r\n{\"key\": \"value\"}\r\n```";
115        let result = extract_json_from_possible_backticks_block(content);
116        pretty_assert_eq!(result, "{\"key\": \"value\"}");
117    }
118
119    #[traced_test]
120    fn test_extract_json_whitespace_after_marker() {
121        let content = "```json     \n{\"key\": \"value\"}\n```";
122        let result = extract_json_from_possible_backticks_block(content);
123        pretty_assert_eq!(result, "{\"key\": \"value\"}");
124    }
125
126    #[traced_test]
127    fn test_extract_json_random_text_instead_of_json() {
128        let content = "```json\nsome random text\n```";
129        let result = extract_json_from_possible_backticks_block(content);
130        pretty_assert_eq!(result, "some random text");
131    }
132}