remove regex dependency, rename constructor, implement Gemtext trait

This commit is contained in:
yggverse 2025-03-16 15:43:27 +02:00
parent 9d27cdfb49
commit 7802869d0d
2 changed files with 70 additions and 20 deletions

View file

@ -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<Self> {
// Parse line
let regex = Regex::split_simple(
r"^(#{1,3})\s*(.+)$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Result
pub fn parse(line: &str) -> Option<Self> {
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<Level>;
/// 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<Level> {
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()
)
}
}