rumdl_lib/utils/
kramdown_utils.rs1use regex::Regex;
7use std::sync::LazyLock;
8
9static SPAN_IAL_PATTERN: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\{[:\.#][^}]*\}$").unwrap());
11
12static EXTENSION_OPEN_PATTERN: LazyLock<Regex> =
15 LazyLock::new(|| Regex::new(r"^\s*\{::([a-z]+)(?:\s+[^}]*)?\}\s*$").unwrap());
16
17static EXTENSION_SELF_CLOSING_PATTERN: LazyLock<Regex> =
19 LazyLock::new(|| Regex::new(r"^\s*\{::[a-z]+(?:\s+[^}]*)?\s*/\}\s*$").unwrap());
20
21static EXTENSION_CLOSE_PATTERN: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^\s*\{:/([a-z]+)?\}\s*$").unwrap());
23
24pub fn is_kramdown_block_attribute(line: &str) -> bool {
47 let trimmed = line.trim();
48
49 if !trimmed.starts_with('{') || !trimmed.ends_with('}') || trimmed.len() < 3 {
51 return false;
52 }
53
54 let second_char = trimmed.chars().nth(1);
57 matches!(second_char, Some(':') | Some('#') | Some('.'))
58}
59
60pub fn has_span_ial(text: &str) -> bool {
71 SPAN_IAL_PATTERN.is_match(text.trim())
72}
73
74pub fn is_kramdown_extension_self_closing(line: &str) -> bool {
76 EXTENSION_SELF_CLOSING_PATTERN.is_match(line)
77}
78
79pub fn is_kramdown_extension_open(line: &str) -> bool {
83 EXTENSION_OPEN_PATTERN.is_match(line) && !is_kramdown_extension_self_closing(line)
84}
85
86pub fn is_kramdown_extension_close(line: &str) -> bool {
88 EXTENSION_CLOSE_PATTERN.is_match(line)
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_kramdown_class_attributes() {
97 assert!(is_kramdown_block_attribute("{:.wrap}"));
98 assert!(is_kramdown_block_attribute("{:.class-name}"));
99 assert!(is_kramdown_block_attribute("{:.multiple .classes}"));
100 }
101
102 #[test]
103 fn test_kramdown_id_attributes() {
104 assert!(is_kramdown_block_attribute("{:#my-id}"));
105 assert!(is_kramdown_block_attribute("{:#section-1}"));
106 }
107
108 #[test]
109 fn test_kramdown_generic_attributes() {
110 assert!(is_kramdown_block_attribute("{:style=\"color: red\"}"));
111 assert!(is_kramdown_block_attribute("{:data-value=\"123\"}"));
112 }
113
114 #[test]
115 fn test_kramdown_combined_attributes() {
116 assert!(is_kramdown_block_attribute("{:.class #id}"));
117 assert!(is_kramdown_block_attribute("{:#id .class style=\"color: blue\"}"));
118 assert!(is_kramdown_block_attribute("{:.wrap #my-code .highlight}"));
119 }
120
121 #[test]
122 fn test_non_kramdown_braces() {
123 assert!(!is_kramdown_block_attribute("{just some text}"));
124 assert!(!is_kramdown_block_attribute("{not kramdown}"));
125 assert!(!is_kramdown_block_attribute("{ spaces }"));
126 }
127
128 #[test]
129 fn test_edge_cases() {
130 assert!(!is_kramdown_block_attribute("{}"));
131 assert!(!is_kramdown_block_attribute("{"));
132 assert!(!is_kramdown_block_attribute("}"));
133 assert!(!is_kramdown_block_attribute(""));
134 assert!(!is_kramdown_block_attribute("not braces"));
135 }
136
137 #[test]
138 fn test_whitespace_handling() {
139 assert!(is_kramdown_block_attribute(" {:.wrap} "));
140 assert!(is_kramdown_block_attribute("\t{:#id}\t"));
141 assert!(is_kramdown_block_attribute(" {:.class #id} "));
142 }
143
144 #[test]
145 fn test_self_closing_extension_blocks() {
146 assert!(is_kramdown_extension_self_closing("{::options toc_levels=\"2..4\" /}"));
148 assert!(is_kramdown_extension_self_closing("{::comment /}"));
149 assert!(is_kramdown_extension_self_closing("{::nomarkdown this='is' .ignore /}"));
150 assert!(is_kramdown_extension_self_closing(" {::options key=\"val\" /} "));
151
152 assert!(!is_kramdown_extension_self_closing("{::comment}"));
154 assert!(!is_kramdown_extension_self_closing("{::nomarkdown}"));
155 assert!(!is_kramdown_extension_self_closing("{::nomarkdown type='html'}"));
156 }
157
158 #[test]
159 fn test_extension_open_excludes_self_closing() {
160 assert!(is_kramdown_extension_open("{::comment}"));
162 assert!(is_kramdown_extension_open("{::nomarkdown}"));
163 assert!(is_kramdown_extension_open("{::nomarkdown type='html'}"));
164
165 assert!(!is_kramdown_extension_open("{::options toc_levels=\"2..4\" /}"));
167 assert!(!is_kramdown_extension_open("{::comment /}"));
168 }
169}