whistle_proxy_rule_parser/
markdown_values.rs

1/// fork from: https://github.com/hgm-king/prose
2use nom::{
3    branch::alt,
4    bytes::complete::{is_not, tag, take},
5    combinator::{map, not},
6    multi::{many0, many1},
7    sequence::{delimited, preceded, terminated, tuple},
8    IResult,
9};
10
11pub type MarkdownText = Vec<MarkdownInline>;
12
13#[derive(Clone, Debug, PartialEq)]
14pub enum Markdown {
15    Line(MarkdownText),
16    Codeblock(String, String),
17}
18
19#[derive(Clone, Debug, PartialEq)]
20pub enum MarkdownInline {
21    Plaintext(String),
22}
23
24pub fn parse_markdown(i: &str) -> IResult<&str, Vec<Markdown>> {
25    many1(alt((
26        map(parse_code_block, |e| {
27            Markdown::Codeblock(e.0.to_string(), e.1.to_string())
28        }),
29        map(parse_markdown_text, |e| Markdown::Line(e)),
30    )))(i)
31}
32
33fn parse_plaintext(i: &str) -> IResult<&str, String> {
34    map(
35        many1(preceded(not(alt((tag("```"), tag("\n")))), take(1u8))),
36        |vec| vec.join(""),
37    )(i)
38}
39
40fn parse_markdown_inline(i: &str) -> IResult<&str, MarkdownInline> {
41    alt((map(parse_plaintext, |s| MarkdownInline::Plaintext(s)),))(i)
42}
43
44fn parse_markdown_text(i: &str) -> IResult<&str, MarkdownText> {
45    terminated(many0(parse_markdown_inline), tag("\n"))(i)
46}
47
48fn parse_code_block(i: &str) -> IResult<&str, (String, &str)> {
49    tuple((parse_code_block_lang, parse_code_block_body))(i)
50}
51
52fn parse_code_block_body(i: &str) -> IResult<&str, &str> {
53    delimited(tag("\n"), is_not("```"), tag("```"))(i)
54}
55
56fn parse_code_block_lang(i: &str) -> IResult<&str, String> {
57    alt((
58        preceded(tag("```"), parse_plaintext),
59        map(tag("```"), |_| "__UNKNOWN__".to_string()),
60    ))(i)
61}
62
63/// Break md_arr into (lines, codes)
64/// # Examples
65/// ```ignore
66/// let (rest, md_arr) = parse_markdown(input).unwrap();
67/// let (input, codes) = into_parts(md_arr);
68/// ```
69/// 
70pub fn into_parts(md_arr: Vec<Markdown>) -> (String, Vec<(String, String)>) {
71    let mut lines = String::new();
72    let mut codes = vec![];
73    md_arr.iter().for_each(|m| {
74        match m {
75            Markdown::Line(v) => {
76                if v.is_empty() {
77                    lines.push('\n');
78                    return;
79                }
80                match &v[0] {
81                  MarkdownInline::Plaintext(s) => {
82                    lines.push_str(&s);
83                    lines.push('\n');
84                  }
85              }
86            }
87            Markdown::Codeblock(name, value) => {
88              codes.push((name.to_owned(), value.to_owned()));
89            }
90        }
91    });
92    (lines, codes)
93}
94
95#[cfg(test)]
96mod test {
97    use super::*;
98    #[test]
99    fn test_markdown() {
100        let input = r#"
101# oijsdf
102**bold text**
103```rust
104fn main() {
105    println!("Hello, world!");
106}
107```
108**bold**
109```js
110console.log(1234)
111```
112`inline code`
113"#;
114        assert_eq!(
115            parse_markdown(input),
116            Ok((
117                "",
118                vec![
119                    Markdown::Line(vec![]),
120                    Markdown::Line(vec![MarkdownInline::Plaintext("# oijsdf".into())]),
121                    Markdown::Line(vec![MarkdownInline::Plaintext("**bold text**".into())]),
122                    Markdown::Codeblock(
123                        "rust".into(),
124                        "fn main() {\n    println!(\"Hello, world!\");\n}\n".into()
125                    ),
126                    Markdown::Line(vec![]),
127                    Markdown::Line(vec![MarkdownInline::Plaintext("**bold**".into())]),
128                    Markdown::Codeblock("js".into(), "console.log(1234)\n".into()),
129                    Markdown::Line(vec![]),
130                    Markdown::Line(vec![MarkdownInline::Plaintext("`inline code`".into())])
131                ]
132            ))
133        );
134
135        assert_eq!(into_parts(parse_markdown(input).unwrap().1), (
136            "\n# oijsdf\n**bold text**\n\n**bold**\n\n`inline code`\n".into(),
137            vec![("rust".into(), "fn main() {\n    println!(\"Hello, world!\");\n}\n".into()), ("js".into(), "console.log(1234)\n".into())]
138        ));
139
140    }
141}