markdown_code_runner/
markers.rs1use once_cell::sync::Lazy;
4use regex::Regex;
5
6pub fn md_comment(text: &str) -> String {
8 format!("<!-- {} -->", text)
9}
10
11pub const WARNING: &str =
13 "<!-- \u{26A0}\u{FE0F} This content is auto-generated by `markdown-code-runner`. -->";
14
15pub const SKIP: &str = "<!-- CODE:SKIP -->";
17
18pub const CODE_COMMENT_PYTHON_START: &str = "<!-- CODE:START -->";
20
21pub const CODE_COMMENT_BASH_START: &str = "<!-- CODE:BASH:START -->";
23
24pub const CODE_COMMENT_END: &str = "<!-- CODE:END -->";
26
27pub const OUTPUT_START: &str = "<!-- OUTPUT:START -->";
29
30pub const OUTPUT_END: &str = "<!-- OUTPUT:END -->";
32
33pub static SKIP_PATTERN: Lazy<Regex> =
37 Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)<!-- CODE:SKIP -->").unwrap());
38
39pub static CODE_COMMENT_PYTHON_START_PATTERN: Lazy<Regex> =
41 Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)<!-- CODE:START -->").unwrap());
42
43pub static CODE_COMMENT_BASH_START_PATTERN: Lazy<Regex> =
45 Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)<!-- CODE:BASH:START -->").unwrap());
46
47pub static CODE_COMMENT_END_PATTERN: Lazy<Regex> =
49 Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)<!-- CODE:END -->").unwrap());
50
51pub static OUTPUT_START_PATTERN: Lazy<Regex> =
53 Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)<!-- OUTPUT:START -->").unwrap());
54
55pub static OUTPUT_END_PATTERN: Lazy<Regex> =
57 Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)<!-- OUTPUT:END -->").unwrap());
58
59pub static CODE_BACKTICKS_START_PATTERN: Lazy<Regex> = Lazy::new(|| {
61 Regex::new(r"^(?P<spaces>\s*)```(?P<language>\w+)\s+markdown-code-runner").unwrap()
62});
63
64pub static CODE_BACKTICKS_END_PATTERN: Lazy<Regex> =
66 Lazy::new(|| Regex::new(r"^(?P<spaces>\s*)```\s*$").unwrap());
67
68pub fn is_skip(line: &str) -> bool {
70 SKIP_PATTERN.is_match(line)
71}
72
73pub fn is_output_start(line: &str) -> Option<regex::Match<'_>> {
75 OUTPUT_START_PATTERN.find(line)
76}
77
78pub fn is_output_end(line: &str) -> bool {
80 OUTPUT_END_PATTERN.is_match(line)
81}
82
83pub fn is_code_comment_python_start(line: &str) -> Option<regex::Match<'_>> {
85 CODE_COMMENT_PYTHON_START_PATTERN.find(line)
86}
87
88pub fn is_code_comment_bash_start(line: &str) -> Option<regex::Match<'_>> {
90 CODE_COMMENT_BASH_START_PATTERN.find(line)
91}
92
93pub fn is_code_comment_end(line: &str) -> bool {
95 CODE_COMMENT_END_PATTERN.is_match(line)
96}
97
98pub fn is_code_backticks_start(line: &str) -> Option<regex::Captures<'_>> {
100 CODE_BACKTICKS_START_PATTERN.captures(line)
101}
102
103pub fn is_code_backticks_end(line: &str) -> bool {
105 CODE_BACKTICKS_END_PATTERN.is_match(line)
106}
107
108pub fn remove_md_comment(line: &str) -> Option<String> {
111 let trimmed = line.trim();
112 if trimmed.starts_with("<!-- ") && trimmed.ends_with(" -->") {
113 Some(trimmed[5..trimmed.len() - 4].to_string())
114 } else {
115 None
116 }
117}
118
119pub fn get_indent(line: &str) -> String {
121 let trimmed = line.trim_start();
122 line[..line.len() - trimmed.len()].to_string()
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_md_comment() {
131 assert_eq!(md_comment("test"), "<!-- test -->");
132 }
133
134 #[test]
135 fn test_remove_md_comment() {
136 assert_eq!(
137 remove_md_comment("<!-- This is a comment -->"),
138 Some("This is a comment".to_string())
139 );
140 assert_eq!(remove_md_comment("This is not a comment"), None);
141 }
142
143 #[test]
144 fn test_is_skip() {
145 assert!(is_skip("<!-- CODE:SKIP -->"));
146 assert!(is_skip(" <!-- CODE:SKIP -->"));
147 assert!(!is_skip("some text"));
148 }
149
150 #[test]
151 fn test_get_indent() {
152 assert_eq!(get_indent(" hello"), " ");
153 assert_eq!(get_indent("hello"), "");
154 assert_eq!(get_indent("\t\thello"), "\t\t");
155 }
156
157 #[test]
158 fn test_is_code_backticks_start() {
159 let caps = is_code_backticks_start("```python markdown-code-runner");
160 assert!(caps.is_some());
161 let caps = caps.unwrap();
162 assert_eq!(&caps["language"], "python");
163
164 let caps = is_code_backticks_start(" ```bash markdown-code-runner filename=test.sh");
165 assert!(caps.is_some());
166 let caps = caps.unwrap();
167 assert_eq!(&caps["language"], "bash");
168 assert_eq!(&caps["spaces"], " ");
169
170 assert!(is_code_backticks_start("```python").is_none());
171 }
172}