From 200dd8b451764ba38e3628502cb8d46be0cd72ab Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Fri, 2 Jun 2023 00:26:19 -0400 Subject: [PATCH] Progress on the Gemtext parser --- src/message/mod.rs | 2 +- src/message/parser.rs | 77 ++++++++++++++++++++++++++++++++++--------- src/prelude.rs | 2 +- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/message/mod.rs b/src/message/mod.rs index 327b5f8..1c64aba 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; mod error; mod link; mod parser; -pub use {error::Error, link::Link, parser::Parser}; +pub use {error::Error, link::Link, parser::{GemtextNode, Parser}}; #[derive(Clone, Debug, PartialEq)] pub struct Recipients { diff --git a/src/message/parser.rs b/src/message/parser.rs index b848b6e..2d2234a 100644 --- a/src/message/parser.rs +++ b/src/message/parser.rs @@ -17,13 +17,14 @@ enum State<'a> { #[derive(Clone, Debug, PartialEq)] pub enum GemtextNode { - Sender(Mailbox), + Sender(Vec), Recipients(Recipients), Timestamp(String), Text(String), Heading1(String), Heading2(String), Heading3(String), + ListItem(String), Quote(String), Preformatted(String), Link(Link), @@ -32,13 +33,18 @@ pub enum GemtextNode { impl fmt::Display for GemtextNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Sender(m) => writeln!(f, "< {m}"), + Self::Sender(m) => { + write!(f, "<")?; + m.iter().try_for_each(|m| write!(f, " {m}"))?; + writeln!(f) + }, Self::Recipients(r) => writeln!(f, "{r}"), Self::Timestamp(t) => writeln!(f, "@ {t}"), Self::Text(t) => writeln!(f, "{t}"), Self::Heading1(h) => writeln!(f, "# {h}"), Self::Heading2(h) => writeln!(f, "## {h}"), Self::Heading3(h) => writeln!(f, "### {h}"), + Self::ListItem(l) => writeln!(f, "* {l}"), Self::Quote(q) => writeln!(f, "> {q}"), Self::Preformatted(p) => writeln!(f, "```\n{p}\n```"), Self::Link(l) => writeln!(f, "=> {l}"), @@ -48,27 +54,56 @@ impl fmt::Display for GemtextNode { impl<'a> GemtextNode { fn parse_link(text: &'a str) -> Self { - todo!() - } - - fn parse_prompt(text: &'a str) -> Self { - todo!() + if let Ok(link) = text.parse() { + Self::Link(link) + } else { + Self::Text(text.to_string()) + } } fn parse_heading(text: &'a str) -> Self { - todo!() + if let Some((h, s)) = text.split_once(char::is_whitespace) { + match h { + "#" => Self::Heading1(s.to_string()), + "##" => Self::Heading2(s.to_string()), + "###" => Self::Heading3(s.to_string()), + _ => Self::Text(text.to_string()), + } + } else { + Self::Text(text.to_string()) + } } fn parse_list_item(text: &'a str) -> Self { - todo!() + match text.split_once(char::is_whitespace) { + Some((pre, s)) if pre == "*" => GemtextNode::ListItem(s.to_string()), + _ => GemtextNode::Text(text.to_string()), + } } fn parse_blockquote(text: &'a str) -> Self { - todo!() + match text.split_once(char::is_whitespace) { + Some((prefix, suffix)) if prefix == ">" => GemtextNode::Quote(suffix.to_string()), + _ => GemtextNode::Text(text.to_string()), + } } fn parse_senders(text: &'a str) -> Self { - todo!() + let mut split = text.split_whitespace(); + match split.next() { + Some(s) if s == "<" => { + let mut senders: Vec = vec![]; + for s in split { + if let Ok(m) = s.parse() { + senders.push(m); + } else { + return Self::Text(text.to_string()); + } + } + Self::Sender(senders) + } + _ => Self::Text(text.to_string()) + } } fn parse_recipients(text: &'a str) -> Self { @@ -94,7 +129,7 @@ impl<'a> Parser<'a> { } } - fn parse(mut self, raw: &'a str) -> Vec { + pub fn parse(mut self, raw: &'a str) -> Vec { for line in raw.lines() { match self.state { State::Normal => self.parse_normal(line), @@ -105,11 +140,23 @@ impl<'a> Parser<'a> { self.lines } + fn link(&mut self, line: &'a str) { + self.lines.push(GemtextNode::parse_link(line)); + } + + fn heading(&mut self, line: &'a str) { + self.lines.push(GemtextNode::parse_heading(line)); + } + + fn list_item(&mut self, line: &'a str) { + self.lines.push(GemtextNode::parse_list_item(line)); + } + fn parse_normal(&mut self, line: &'a str) { match line { - s if s.starts_with("=>") => {}, - s if s.starts_with('#') => {}, - s if s.starts_with('*') => {}, + s if s.starts_with("=>") => self.link(s), + s if s.starts_with('#') => self.heading(s), + s if s.starts_with('*') => self.list_item(s), s if s.starts_with('>') => {}, s if s.starts_with("```") => {}, s if s.starts_with('<') => {}, diff --git a/src/prelude.rs b/src/prelude.rs index e0ec573..f89579c 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,7 +4,7 @@ pub use super::{ host::{Error as ParseHostError, Host}, mailbox::{Error as ParseMailboxError, Mailbox}, mailuser::Mailuser, - message::{Error as ParseMessageError, Lines, Message, Recipients}, + message::{Error as ParseMessageError, GemtextNode, Message, Recipients}, //receiver, request::{Error as ParseRequestError, Request}, response::{Error as ParseResponseError, Response},