1use wasm_bindgen::prelude::*;
2
3fn get_lines(md: &str) -> Vec<String> {
4 let mut lines: Vec<String> = Vec::new();
5 let mut line = String::from("");
6 for c in md.chars() {
7 if c == '\n' {
8 if !line.is_empty() {
9 lines.push(line.clone());
10 }
11 line.clear();
12 } else {
13 line.push(c);
14 }
15 }
16
17 if !line.is_empty() {
18 lines.push(line);
19 }
20
21 lines
22}
23
24fn close_list_tags(tag_ul: &mut bool, tag_ol: &mut bool, html: &mut String) {
25 if *tag_ul {
26 html.push_str("</ul>");
27 *tag_ul = false;
28 }
29 if *tag_ol {
30 html.push_str("</ol>");
31 *tag_ol = false;
32 }
33}
34
35fn process_line(l: &str) -> String {
36 let mut chars = l.chars().peekable();
37 let mut line = String::from("");
38
39 let mut tag_code = false;
40 let mut tag_b = false;
41 let mut tag_i = false;
42
43 while let Some(c) = chars.next() {
44 if c == '`' {
45 tag_code = !tag_code;
46 if tag_code {
47 line.push_str("<code>");
48 } else {
49 line.push_str("</code>");
50 }
51 } else if c == '*' {
52 if chars.peek() == Some(&'*') {
53 chars.next();
54 tag_b = !tag_b;
55 if tag_b {
56 line.push_str("<b>");
57 } else {
58 line.push_str("</b>");
59 }
60 }
61 } else if c == '_' {
62 tag_i = !tag_i;
63 if tag_i {
64 line.push_str("<i>");
65 } else {
66 line.push_str("</i>");
67 }
68 } else if c == '[' {
69 let mut text = String::new();
70 while let Some(&next) = chars.peek() {
71 if next == ']' {
72 chars.next();
73 break;
74 }
75 text.push(next);
76 chars.next();
77 }
78
79 if chars.peek() == Some(&'(') {
80 chars.next();
81 let mut url = String::new();
82 while let Some(&next) = chars.peek() {
83 if next == ')' {
84 chars.next();
85 break;
86 }
87 url.push(next);
88 chars.next();
89 }
90
91 if !url.is_empty() {
92 line.push_str(&format!(
93 "<a class=\"link\" href=\"{url}\" target=\"_blank\">{text}</a>"
94 ));
95 } else {
96 line.push('[');
97 line.push_str(&text);
98 line.push(']');
99 }
100 } else {
101 line.push('[');
102 line.push_str(&text);
103 line.push(']');
104 }
105 } else {
106 line.push(c);
107 }
108 }
109
110 line.to_string()
111}
112
113#[wasm_bindgen]
114pub fn parse(md: String) -> String {
115 let mut html = String::from("");
116
117 let lines: Vec<String> = get_lines(&md);
118
119 let mut tag_pre = false;
120 let mut tag_ul = false;
121 let mut tag_ol = false;
122
123 for l in lines.iter() {
124 for (j, c) in l.chars().enumerate() {
125 if j == 0 && c == '#' {
126 close_list_tags(&mut tag_ul, &mut tag_ol, &mut html);
128 let count_hash = l.chars().filter(|&c| c == '#').count();
129 let line = process_line(&l[count_hash + 1..]);
130 let line = format!("<h{count_hash}>{line}</h{count_hash}>");
131 html.push_str(&line);
132 } else if j == 0 && c == '>' {
133 close_list_tags(&mut tag_ul, &mut tag_ol, &mut html);
135 let line = process_line(&l[2..]);
136 let line = format!("<blockquote>{line}</blockquote>");
137 html.push_str(&line);
138 } else if j == 0 && c == '-' {
139 if l.trim().chars().all(|ch| ch == '-') {
141 html.push_str("<hr>");
142 break;
143 }
144 if !tag_ul {
145 html.push_str("<ul>");
146 }
147 tag_ul = true;
148 let line = process_line(&l[2..]);
149 let line = format!("<li>{line}</li>");
150 html.push_str(&line);
151 } else if j == 0 && c.is_ascii_digit() {
152 if !tag_ol {
154 html.push_str("<ol>");
155 }
156 tag_ol = true;
157 let line = process_line(&l[3..]);
158 let line = format!("<li>{line}</li>");
159 html.push_str(&line);
160 } else if j == 0 && c == '`' {
161 close_list_tags(&mut tag_ul, &mut tag_ol, &mut html);
163 let count_backtick = l.chars().filter(|&c| c == '`').count();
164 if count_backtick == 3 {
165 tag_pre = !tag_pre;
166 if tag_pre {
167 html.push_str("<pre>");
168 } else {
169 html.push_str("</pre>");
170 }
171 } else if count_backtick % 2 == 0 {
172 let line = process_line(l);
173 let line = format!("<p>{line}</p>");
174 html.push_str(&line);
175 }
176 } else if j == 0 && tag_pre {
177 let line = format!("<code>{l}</code>\n");
179 html.push_str(&line);
180 } else if j == 0 && !tag_pre {
181 close_list_tags(&mut tag_ul, &mut tag_ol, &mut html);
183 let line = process_line(l);
184 let line = format!("<p>{line}</p>");
185 html.push_str(&line);
186 }
187 }
188 }
189
190 html
191}