switchbot_api/
markdown.rs1use std::{fmt::Display, sync::LazyLock};
2
3use regex::Regex;
4
5#[derive(Clone, Debug, Default)]
13pub struct Markdown {
14 markdown: String,
15}
16
17impl Markdown {
18 pub fn new(markdown: &str) -> Self {
19 Self {
20 markdown: markdown.to_string(),
21 }
22 }
23
24 pub fn markdown(&self) -> &str {
26 &self.markdown
27 }
28
29 pub(crate) fn em(text: &str) -> Option<&str> {
30 const RE_EM_PAT: &str = r"\*([^*]+)\*";
31 static RE_EM: LazyLock<Regex> = LazyLock::new(|| Regex::new(RE_EM_PAT).unwrap());
32 if let Some(captures) = RE_EM.captures(text) {
33 return captures.get(1).map(|m| m.as_str());
34 }
35 None
36 }
37
38 fn plain_text(&self) -> String {
39 const RE_BR_PAT: &str = r"(?i)<br\s*/?>";
40 static RE_BR: LazyLock<Regex> = LazyLock::new(|| Regex::new(RE_BR_PAT).unwrap());
41 RE_BR.replace_all(&self.markdown, "\n").into()
42 }
43
44 pub(crate) fn table_columns(line: &str) -> Option<Vec<&str>> {
45 if line.starts_with('|') && line.ends_with('|') {
46 let columns = line
47 .split_terminator('|')
48 .skip(1)
49 .map(|s| s.trim())
50 .collect::<Vec<&str>>();
51 return Some(columns);
52 }
53 None
54 }
55}
56
57impl Display for Markdown {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "{}", self.plain_text())
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn em() {
69 assert_eq!(Markdown::em("*a*"), Some("a"));
70 assert_eq!(Markdown::em("x*a*x"), Some("a"));
71
72 assert_eq!(Markdown::em("a"), None);
73 assert_eq!(Markdown::em("*a"), None);
74 assert_eq!(Markdown::em("a*"), None);
75
76 assert_eq!(
77 Markdown::em("device type. *Hub*, *Hub Plus*, *Hub Mini*, *Hub 2* or *Hub 3*."),
78 Some("Hub")
79 );
80 }
81
82 fn to_plain_text(markdown: &str) -> String {
83 Markdown::new(markdown).plain_text()
84 }
85
86 #[test]
87 fn plain_text() {
88 assert_eq!(to_plain_text(""), "");
89
90 assert_eq!(to_plain_text("<br>"), "\n");
91 assert_eq!(to_plain_text("<br/>"), "\n");
92 assert_eq!(to_plain_text("<br />"), "\n");
93 assert_eq!(to_plain_text("<BR>"), "\n");
94
95 assert_eq!(to_plain_text("a<br>b"), "a\nb");
96
97 assert_eq!(to_plain_text("a<br>b<br>c"), "a\nb\nc");
98 }
99
100 fn to_table_columns(line: &str) -> Option<Vec<&str>> {
101 Markdown::table_columns(line)
102 }
103
104 #[test]
105 fn table_columns() {
106 assert_eq!(to_table_columns("1|2|3"), None);
107 assert_eq!(to_table_columns("|1|2|3|"), Some(vec!["1", "2", "3"]));
108 assert_eq!(to_table_columns("| 1 | 2 | 3 |"), Some(vec!["1", "2", "3"]));
109 }
110}