use std::error::Error;
use std::fmt;
use std::fmt::Write as _;
use std::fmt::{Debug, Formatter};
#[derive(Debug)]
struct ParseGmiErr;
impl fmt::Display for ParseGmiErr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Failed parsing Gemini to HTML format!")
}
}
impl Error for ParseGmiErr {}
pub fn parse_to_html(gmi: &str) -> Result<Vec<String>, Box<dyn Error>> {
let mut result = Vec::new();
let (mut cb_bool, mut cb_text) = (false, String::new());
let (mut list_bool, mut list_text) = (false, String::new());
'main: for line in gmi.lines() {
let line = line.trim_start();
if let Some(strip) = line.strip_prefix("* ") {
write!(list_text, "<li>{strip}</li>")?;
list_bool = true;
continue
}
if list_bool {
result.push(format!("<ul>{list_text}</ul>"));
list_bool = false;
list_text.clear();
}
if line == "```" {
result.push(cb_text.clone() + "</code>");
cb_text.clear();
cb_bool = false;
continue;
}
if cb_bool {
writeln!(cb_text, "{line}")?;
continue;
}
if let Some(strip) = line.strip_prefix("# ") {
result.push(format!("<h1>{strip}</h1>"));
continue;
}
if let Some(strip) = line.strip_prefix("## ") {
result.push(format!("<h2>{strip}</h2>"));
continue;
}
if let Some(strip) = line.strip_prefix("### ") {
result.push(format!("<h3>{strip}</h3>"));
continue;
}
if let Some(strip) = line.strip_prefix("=> ") {
let url = strip.split_whitespace().next().ok_or(ParseGmiErr)?;
let text = strip.strip_prefix(url).ok_or(ParseGmiErr)?.trim_start();
let image_formats = [".jpeg", ".png", ".jpg", ".webp", ".gif", ".svg"];
for format in image_formats {
if url.ends_with(format) {
result.push(format!("<img src=\"{url}\" alt=\"{text}\">"));
continue 'main
}
}
result.push(format!("<a href=\"{url}\">{text}</a>"));
continue;
}
if let Some(strip) = line.strip_prefix("> ") {
result.push(format!("<blockquote><p>{strip}</p></blockquote>"));
continue;
}
if let Some(strip) = line.strip_prefix("```") {
writeln!(cb_text, "<code> {strip}")?;
cb_bool = true;
continue;
}
result.push(format!("<p>{line}</p>"));
}
Ok(result)
}