use std::fmt::Write;
#[derive(Debug)]
pub enum Symbol {
Text(String),
Link(String, String),
List(String),
Quote(String),
Header1(String),
Header2(String),
Header3(String),
Codeblock(String, String),
}
#[derive(Debug)]
pub struct Gemtext(pub Vec<Symbol>);
impl std::fmt::Display for Gemtext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for s in &self.0 {
match s {
Symbol::Text(a) => writeln!(f, "{a}")?,
Symbol::Link(a, b) => writeln!(f, "=> {a} {b}")?,
Symbol::List(a) => writeln!(f, "* {a}")?,
Symbol::Quote(a) => writeln!(f, "> {a}")?,
Symbol::Header1(a) => writeln!(f, "# {a}")?,
Symbol::Header2(a) => writeln!(f, "## {a}")?,
Symbol::Header3(a) => writeln!(f, "### {a}")?,
Symbol::Codeblock(a, b) => write!(f, "``` {a}\n{b}\n```\n")?,
}
}
Ok(())
}
}
impl Gemtext {
pub fn inner(self) -> Vec<Symbol> {
self.0
}
pub fn parse(s: &str) -> Self {
let mut v: Vec<Symbol> = Vec::new();
let mut lines = s.lines();
loop {
let Some(x) = lines.next() else {
break;
};
if let Some(x) = x.strip_prefix("=>") {
if let Some((link, name)) = x.trim().split_once(' ') {
v.push(Symbol::Link(link.to_string(), name.trim().to_string()))
} else {
v.push(Symbol::Link(x.trim().to_string(), String::new()))
}
continue;
}
if let Some(x) = x.strip_prefix('*') {
v.push(Symbol::List(x.trim().to_string()));
continue;
}
if let Some(x) = x.strip_prefix('>') {
v.push(Symbol::Quote(x.trim().to_string()));
continue;
}
if let Some(x) = x.strip_prefix("###") {
v.push(Symbol::Header3(x.trim().to_string()));
continue;
}
if let Some(x) = x.strip_prefix("##") {
v.push(Symbol::Header2(x.trim().to_string()));
continue;
}
if let Some(x) = x.strip_prefix('#') {
v.push(Symbol::Header1(x.trim().to_string()));
continue;
}
if let Some(x) = x.strip_prefix("```") {
let alt_text = x.trim().to_string();
let mut block: Vec<&str> = Vec::new();
loop {
let Some(x) = lines.next() else {
break;
};
if x.starts_with("```") {
break;
}
block.push(x);
}
v.push(Symbol::Codeblock(alt_text, {
let mut s = block.into_iter().fold(String::new(), |mut out, x| {
let _ = writeln!(out, "{x}");
out
});
s.pop(); s
}));
continue;
}
v.push(Symbol::Text(x.to_string()));
}
Gemtext(v)
}
}