1#![allow(unused)]
2
3#[derive(Debug, Clone)]
4pub struct Link {
5 link_text: String,
6 href: String,
7 title: String,
8 kind: u8, }
10
11#[derive(Debug, Clone)]
12pub enum Span {
13 Code(String),
14 Math(String),
15 Strong(String),
16 Link(Link),
17 Text(String),
18}
19
20impl Span {
21 pub fn tohtml(&self) -> String {
22 match &*self {
23 Self::Code(text) => {
24 format!("<code>{}</code>", text)
25 }
26 Self::Math(text) => {
27 format!("\\({}\\)", text)
28 }
29 Self::Strong(text) => {
30 format!("<strong>{}</strong>", text)
31 }
32 Self::Link(link) => {
33 match link.kind {
34 1 => {
35 format!(
37 "<img src=\"{}\" alt=\"{}\" title=\"{}\">",
38 link.href, link.link_text, link.title
39 )
40 }
41 _ => {
42 format!(
43 "<a href=\"{}\" title=\"{}\">{}</a>",
44 link.href, link.title, link.link_text,
45 )
46 }
47 }
48 }
49 Self::Text(text) => {
50 format!("{}", text)
51 }
52 _ => {
53 format!("")
54 }
55 }
56 }
57}
58
59pub fn parse_inline(input: &String) -> Vec<Span> {
60 let mut spans = Vec::new();
61
62 let mut open_tag = vec![];
64 let mut indexs: Vec<usize> = Vec::new();
65 let mut tags: Vec<char> = Vec::new();
66 for (_index, _tag) in input.char_indices() {
67 match _tag {
68 '`' => match open_tag.last() {
69 Some('`') => {
70 open_tag.pop();
71 indexs.push(_index);
72 tags.push(_tag);
73 }
74 None => {
75 open_tag.push('`');
76 indexs.push(_index);
77 tags.push(_tag);
78 }
79 _ => {}
80 },
81 '$' => match open_tag.last() {
82 Some('$') => {
83 open_tag.pop();
84 indexs.push(_index);
85 tags.push(_tag);
86 }
87 None => {
88 open_tag.push('$');
89 indexs.push(_index);
90 tags.push(_tag);
91 }
92 _ => {}
93 },
94 '!' => match open_tag.last() {
95 None => {
96 indexs.push(_index);
97 tags.push(_tag);
98 }
99 _ => {}
100 },
101 '*' => match open_tag.last() {
102 Some('*') => {
103 open_tag.pop();
104 indexs.push(_index);
105 tags.push(_tag);
106 }
107 None => {
108 open_tag.push('*');
109 indexs.push(_index);
110 tags.push(_tag);
111 }
112 _ => {}
113 },
114 '[' => match open_tag.last() {
115 None => {
116 open_tag.push(_tag);
117 indexs.push(_index);
118 tags.push(_tag);
119 }
120 _ => {}
121 },
122 ']' => match open_tag.last() {
123 Some('[') => {
124 open_tag.pop();
125 indexs.push(_index);
126 tags.push(_tag);
127 }
128 _ => {}
129 },
130 '(' => match open_tag.last() {
131 None => {
132 open_tag.push(_tag);
133 indexs.push(_index);
134 tags.push(_tag);
135 }
136 _ => {}
137 },
138 ')' => match open_tag.last() {
139 Some('(') => {
140 open_tag.pop();
141 indexs.push(_index);
142 tags.push(_tag);
143 }
144 _ => {}
145 },
146 '<' => match open_tag.last() {
147 None => {
148 open_tag.push(_tag);
149 indexs.push(_index);
150 tags.push(_tag);
151 }
152 _ => {}
153 },
154 '>' => match open_tag.last() {
155 Some('<') => {
156 open_tag.pop();
157 indexs.push(_index);
158 tags.push(_tag);
159 }
160 _ => {}
161 },
162 '"' => match open_tag.last() {
163 Some('(') => {
164 open_tag.push(_tag);
165 indexs.push(_index);
166 tags.push(_tag);
167 }
168 Some('"') => {
169 open_tag.pop();
170 indexs.push(_index);
171 tags.push(_tag);
172 }
173 _ => {}
174 },
175 _ => {}
176 }
177 }
178
179 let mut idx = 0usize;
180 let mut last = 0usize;
181 let max_len = indexs.len();
182 while idx < max_len {
183 if tags[idx] == '$' && idx + 1 < max_len && tags[idx + 1] == '$' {
184 if indexs[idx] - last > 0 {
186 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
187 }
188 spans.push(Span::Math(String::from(
190 &input[indexs[idx] + 1..indexs[idx + 1]],
191 )));
192 last = indexs[idx + 1] + 1;
194 idx += 2;
195 continue;
196 } else if tags[idx] == '`' && idx + 1 < max_len && tags[idx + 1] == '`' {
197 if indexs[idx] - last > 0 {
199 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
200 }
201 spans.push(Span::Code(String::from(
203 &input[indexs[idx] + 1..indexs[idx + 1]],
204 )));
205 last = indexs[idx + 1] + 1;
207 idx += 2;
208 continue;
209 } else if tags[idx] == '<' && idx + 1 < max_len && tags[idx + 1] == '>' {
210 if indexs[idx] - last > 0 {
212 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
213 }
214 let text = String::from(&input[indexs[idx] + 1..indexs[idx + 1]]);
216 spans.push(Span::Link(Link {
217 link_text: text.to_owned(),
218 href: text.to_owned(),
219 title: text.to_owned(),
220 kind: 0,
221 }));
222 last = indexs[idx + 1] + 1;
224 idx += 2;
225 continue;
226 } else if tags[idx] == '*'
227 && idx + 3 < max_len
228 && tags[idx + 1] == '*'
229 && tags[idx + 2] == '*'
230 && tags[idx + 3] == '*'
231 && indexs[idx + 1] == indexs[idx] + 1
232 && indexs[idx + 3] == indexs[idx + 2] + 1
233 {
234 if indexs[idx] - last > 0 {
236 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
237 }
238 spans.push(Span::Strong(String::from(
240 &input[indexs[idx + 1] + 1..indexs[idx + 2]],
241 )));
242 last = indexs[idx + 3] + 1;
244 idx += 4;
245 continue;
246 } else if tags[idx] == '['
247 && idx + 3 < max_len
248 && tags[idx + 1] == ']'
249 && tags[idx + 2] == '('
250 && tags[idx + 3] == ')'
251 && indexs[idx + 2] == indexs[idx + 1] + 1
252 {
253 if indexs[idx] - last > 0 {
255 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
256 }
257 spans.push(Span::Link(Link {
259 link_text: String::from(&input[indexs[idx] + 1..indexs[idx + 1]]),
260 href: String::from(&input[indexs[idx + 2] + 1..indexs[idx + 3]]),
261 title: String::from(""),
262 kind: 0,
263 }));
264 last = indexs[idx + 3] + 1;
266 idx += 4;
267 continue;
268 } else if tags[idx] == '['
269 && idx + 3 < max_len
270 && tags[idx + 1] == ']'
271 && tags[idx + 2] == '('
272 && tags[idx + 3] == '\"'
273 && tags[idx + 4] == '\"'
274 && tags[idx + 5] == ')'
275 && indexs[idx + 2] == indexs[idx + 1] + 1
276 {
277 if indexs[idx] - last > 0 {
279 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
280 }
281 spans.push(Span::Link(Link {
283 link_text: String::from(&input[indexs[idx] + 1..indexs[idx + 1]]),
284 href: String::from(&input[indexs[idx + 2] + 1..indexs[idx + 3]]),
285 title: String::from(&input[indexs[idx + 3] + 1..indexs[idx + 4]]),
286 kind: 0,
287 }));
288 last = indexs[idx + 5] + 1;
290 idx += 6;
291 continue;
292 } else if tags[idx] == '!'
293 && idx + 4 < max_len
294 && tags[idx + 1] == '['
295 && tags[idx + 2] == ']'
296 && tags[idx + 3] == '('
297 && tags[idx + 4] == ')'
298 && indexs[idx + 1] == indexs[idx] + 1
299 && indexs[idx + 3] == indexs[idx + 2] + 1
300 {
301 if indexs[idx] - last > 0 {
303 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
304 }
305 spans.push(Span::Link(Link {
307 link_text: String::from(&input[indexs[idx + 1] + 1..indexs[idx + 2]]),
308 href: String::from(&input[indexs[idx + 3] + 1..indexs[idx + 4]]),
309 title: String::from(""),
310 kind: 1,
311 }));
312 last = indexs[idx + 4] + 1;
314 idx += 5;
315 continue;
316 } else if tags[idx] == '!'
317 && idx + 4 < max_len
318 && tags[idx + 1] == '['
319 && tags[idx + 2] == ']'
320 && tags[idx + 3] == '('
321 && tags[idx + 4] == '\"'
322 && tags[idx + 5] == '\"'
323 && tags[idx + 6] == ')'
324 && indexs[idx + 1] == indexs[idx] + 1
325 && indexs[idx + 3] == indexs[idx + 2] + 1
326 {
327 if indexs[idx] - last > 0 {
329 spans.push(Span::Text(String::from(&input[last..indexs[idx]])));
330 }
331 spans.push(Span::Link(Link {
333 link_text: String::from(&input[indexs[idx + 1] + 1..indexs[idx + 2]]),
334 href: String::from(&input[indexs[idx + 3] + 1..indexs[idx + 4]]),
335 title: String::from(&input[indexs[idx + 4] + 1..indexs[idx + 5]]),
336 kind: 1,
337 }));
338 last = indexs[idx + 6] + 1;
340 idx += 7;
341 continue;
342 } else {
343 idx += 1;
345 }
346 }
347
348 let the_rest = String::from(&input[last..]);
349 if the_rest.len() > 0 {
350 spans.push(Span::Text(the_rest));
351 }
352
353 spans
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use std::fs;
361
362 #[test]
363 #[ignore]
364 fn test_inline() {
365 let input = String::from("`hello` :+1: hello **我是粗体**,`markx` is good,哈哈。$\\pi$,[链接](/hello/word \"hello\") ,解析我");
367
368 let spans = parse_inline(&input);
369 println!("{:?}", spans);
370 }
371
372 #[test]
373 #[ignore]
374 fn test_inline2() {
375 let input = String::from("`int(input(\"请输入你猜的数字:\"))`:`input()` 表示输出一段提示语句,然后以字符串形式接收你输入的内容,`int()` 表示把输入的内容转化为整数。");
376 let spans = parse_inline(&input);
377 println!("{:?}", spans);
378 }
379 #[test]
380 fn span_tohtml() {
381 let c = Span::Code("fn".to_string());
382 let m = Span::Math("f(x)".to_string());
383 let t = Span::Text("f(x)".to_string());
384 let l = Span::Link(Link {
385 link_text: "More".to_string(),
386 href: "/more".to_string(),
387 title: "More".to_string(),
388 kind: 0,
389 });
390 let i = Span::Link(Link {
391 link_text: "上海鲜花港 - 郁金香".to_string(),
392 href: "https://www.w3school.com.cn/i/eg_tulip.jpg".to_string(),
393 title: "上海鲜花港 - 郁金香".to_string(),
394 kind: 1,
395 });
396 println!("{}", c.tohtml());
397 println!("{}", m.tohtml());
398 println!("{}", t.tohtml());
399 println!("{}", l.tohtml());
400 println!("{}", i.tohtml());
401 }
402}