From 7802869d0db82aa83504a0bf76eafdc668460756 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 16 Mar 2025 15:43:27 +0200 Subject: [PATCH] remove regex dependency, rename constructor, implement Gemtext trait --- src/line/header.rs | 88 ++++++++++++++++++++++++++++++++++---------- tests/integration.rs | 2 +- 2 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/line/header.rs b/src/line/header.rs index e764931..061ce34 100644 --- a/src/line/header.rs +++ b/src/line/header.rs @@ -1,4 +1,8 @@ -use glib::{Regex, RegexCompileFlags, RegexMatchFlags}; +/// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) tag +/// * store as entire static chars array +pub const TAG_H1: &str = "#"; +pub const TAG_H2: &str = "##"; +pub const TAG_H3: &str = "###"; /// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) type holder pub enum Level { @@ -9,32 +13,78 @@ pub enum Level { /// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) entity holder pub struct Header { - pub value: String, pub level: Level, + pub value: String, } impl Header { // Constructors /// Parse `Self` from line string - pub fn from(line: &str) -> Option { - // Parse line - let regex = Regex::split_simple( - r"^(#{1,3})\s*(.+)$", - line, - RegexCompileFlags::DEFAULT, - RegexMatchFlags::DEFAULT, - ); - - // Result + pub fn parse(line: &str) -> Option { Some(Self { - level: match regex.get(1)?.len() { - 1 => Level::H1, - 2 => Level::H2, - 3 => Level::H3, - _ => return None, - }, - value: regex.get(2)?.trim().to_string(), + level: line.to_level()?, + value: line.as_value()?.to_string(), }) } + + // Converters + + /// Convert `Self` to [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) line + pub fn to_source(&self) -> String { + self.value.to_source(&self.level) + } +} + +pub trait Gemtext { + /// Get [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) value from `Self` + fn as_value(&self) -> Option<&Self>; + /// Convert `Self` to `Level` + fn to_level(&self) -> Option; + /// Convert `Self` to [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) line + fn to_source(&self, level: &Level) -> String; +} + +impl Gemtext for str { + fn as_value(&self) -> Option<&Self> { + if let Some(h3) = self.strip_prefix(TAG_H3) { + if h3.trim_start().starts_with(TAG_H1) { + return None; // H4+ + } + return Some(h3.trim()); + } + if let Some(h2) = self.strip_prefix(TAG_H2) { + return Some(h2.trim()); + } + if let Some(h1) = self.strip_prefix(TAG_H1) { + return Some(h1.trim()); + } + None + } + fn to_level(&self) -> Option { + if let Some(h3) = self.strip_prefix(TAG_H3) { + if h3.trim_start().starts_with(TAG_H1) { + return None; // H4+ + } + return Some(Level::H3); + } + if self.starts_with(TAG_H2) { + return Some(Level::H2); + } + if self.starts_with(TAG_H1) { + return Some(Level::H1); + } + None + } + fn to_source(&self, level: &Level) -> String { + format!( + "{} {}", + match level { + Level::H1 => TAG_H1, + Level::H2 => TAG_H2, + Level::H3 => TAG_H3, + }, + self.trim() + ) + } } diff --git a/tests/integration.rs b/tests/integration.rs index 90c85dc..70924f6 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -60,7 +60,7 @@ fn gemtext() { }; // Header - if let Some(result) = Header::from(line) { + if let Some(result) = Header::parse(line) { headers.push(result); continue; }