]> git.r.bdr.sh - rbdr/page/blob - src/gemini_parser.rs
d0715f102246aef12dbeade159c285b9d856ccd8
[rbdr/page] / src / gemini_parser.rs
1 pub fn parse(source: &str) -> String {
2
3 let lines = source.split("\n");
4 let mut is_preformatted = false;
5
6 let mut html:String = "".to_owned();
7 let mut current_line_type: Option<LineType> = None;
8
9 for line in lines {
10 let mut line_type = LineType::Text;
11 if line.char_indices().count() > 2 {
12 let mut end = line.len();
13 if line.char_indices().count() > 3 {
14 end = line.char_indices().map(|(i, _)| i).nth(3).unwrap();
15 }
16 line_type = identify_line(&line[..end], is_preformatted);
17 }
18 match line_type {
19 LineType::PreformattedToggle => is_preformatted = !is_preformatted,
20 _ => {
21 // Close previous block if needed
22 if let Some(line) = &current_line_type {
23 if line != &line_type && is_block(line) {
24 html.push_str(get_line_closer(line));
25 }
26 }
27
28 // Blocks
29 if is_block(&line_type) {
30 if let Some(line) = &current_line_type {
31 if line != &line_type {
32 html.push_str(get_line_opener(&line_type));
33 }
34 } else {
35 html.push_str(get_line_opener(&line_type));
36 }
37
38 let line_content = get_partial_line_content(&line_type, line);
39 html.push_str(&line_content);
40 } else {
41 let line_content = get_full_line_content(&line_type, line);
42 html.push_str(&line_content);
43 }
44 current_line_type = Some(line_type);
45 },
46 }
47 }
48 if let Some(line) = &current_line_type {
49 if is_block(line) {
50 html.push_str(get_line_closer(line));
51 }
52 }
53 html
54 }
55
56 fn is_block(line_type: &LineType) -> bool {
57 return match line_type {
58 LineType::PreformattedText => true,
59 LineType::ListItem => true,
60 LineType::Quote => true,
61 _ => false,
62 }
63 }
64
65 fn get_partial_line_content(line_type: &LineType, line: &str) -> String {
66 return match line_type {
67 LineType::ListItem => format!("<li>{}</li>", line[2..].trim()),
68 LineType::Quote => line[1..].trim().to_string(),
69 LineType::PreformattedText => format!("{}\n", line),
70 _ => "".to_string(),
71 }
72 }
73
74 fn get_full_line_content(line_type: &LineType, line: &str) -> String {
75 match line_type {
76 LineType::Text => format!("<p>{}</p>\n", line.trim()),
77 LineType::Blank => "<br/>\n".to_string(),
78 LineType::Link => {
79 let url = get_link_address(line);
80 if url.starts_with("gemini:") {
81 format!("<div><a href=\"{}\">{}</a></div>\n", url, get_link_content(line))
82 } else {
83 format!("<div><a href=\"{}\">{}</a></div>\n", url.replace(".gmi", ".html"), get_link_content(line))
84 }
85 },
86 LineType::Heading1 => format!("<h1>{}</h1>\n", line[1..].trim()),
87 LineType::Heading2 => format!("<h2>{}</h2>\n", line[2..].trim()),
88 LineType::Heading3 => format!("<h3>{}</h3>\n", line[3..].trim()),
89 _ => "".to_string(),
90 }
91 }
92
93 fn get_line_opener(line_type: &LineType) -> &'static str {
94 match line_type {
95 LineType::ListItem => "<ul>",
96 LineType::Quote => "<blockquote>",
97 LineType::PreformattedText => "<pre>",
98 _ => "",
99 }
100 }
101
102 fn get_line_closer(line_type: &LineType) -> &'static str {
103 match line_type {
104 LineType::ListItem => "</ul>\n",
105 LineType::Quote => "</blockquote>\n",
106 LineType::PreformattedText => "</pre>\n",
107 _ => "",
108 }
109 }
110
111 fn get_link_content(line: &str) -> &str {
112 let components: Vec<&str> = line[2..].trim().splitn(2, " ").collect();
113 if components.len() > 1 {
114 return components[1].trim()
115 }
116 components[0].trim()
117 }
118
119 fn get_link_address(line: &str) -> &str {
120 let components: Vec<&str> = line[2..].trim().splitn(2, " ").collect();
121 components[0].trim()
122 }
123
124 fn identify_line(line: &str, is_preformatted: bool) -> LineType {
125 if line.starts_with("```") {
126 return LineType::PreformattedToggle;
127 }
128 if is_preformatted {
129 return LineType::PreformattedText;
130 }
131 if line.is_empty() {
132 return LineType::Blank;
133 }
134 if line.starts_with("=>") {
135 return LineType::Link;
136 }
137 if line.starts_with("* ") {
138 return LineType::ListItem;
139 }
140 if line.starts_with(">") {
141 return LineType::Quote;
142 }
143 if line.starts_with("###") {
144 return LineType::Heading3;
145 }
146 if line.starts_with("##") {
147 return LineType::Heading2;
148 }
149 if line.starts_with("#") {
150 return LineType::Heading1;
151 }
152
153 LineType::Text
154 }
155
156 #[derive(PartialEq, Eq)]
157 enum LineType {
158 Text,
159 Blank,
160 Link,
161 PreformattedToggle,
162 PreformattedText,
163 Heading1,
164 Heading2,
165 Heading3,
166 ListItem,
167 Quote
168 }