From 3e16995e005a9b5eb168e3785123c634fa143df3 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 16 Mar 2025 16:39:02 +0200 Subject: [PATCH] reorganize header component --- src/line/header.rs | 91 ++++++++++---------------------------- src/line/header/gemtext.rs | 66 +++++++++++++++++++++++++++ src/line/header/level.rs | 16 +++++++ 3 files changed, 105 insertions(+), 68 deletions(-) create mode 100644 src/line/header/gemtext.rs create mode 100644 src/line/header/level.rs diff --git a/src/line/header.rs b/src/line/header.rs index c2f8647..7745a5d 100644 --- a/src/line/header.rs +++ b/src/line/header.rs @@ -1,15 +1,8 @@ -/// [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 = "###"; +pub mod gemtext; +pub mod level; -/// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) type holder -pub enum Level { - H1, - H2, - H3, -} +pub use gemtext::Gemtext; +pub use level::Level; /// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) entity holder pub struct Header { @@ -22,10 +15,25 @@ impl Header { /// Parse `Self` from line string pub fn parse(line: &str) -> Option { - Some(Self { - level: line.to_level()?, - value: line.as_value()?.to_string(), - }) + if let Some(value) = line.as_h1_value() { + return Some(Self { + level: Level::H1, + value: value.to_string(), + }); + } + if let Some(value) = line.as_h2_value() { + return Some(Self { + level: Level::H2, + value: value.to_string(), + }); + } + if let Some(value) = line.as_h3_value() { + return Some(Self { + level: Level::H3, + value: value.to_string(), + }); + } + None } // Converters @@ -35,56 +43,3 @@ impl Header { self.value.to_source(&self.level) } } - -pub trait Gemtext { - /// Get [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) value for `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<&str> { - 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/src/line/header/gemtext.rs b/src/line/header/gemtext.rs new file mode 100644 index 0000000..51e946a --- /dev/null +++ b/src/line/header/gemtext.rs @@ -0,0 +1,66 @@ +use super::Level; + +pub trait Gemtext { + /// Get [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) value for `Self` + fn as_value(&self) -> Option<&str>; + /// Get parsed H1 header value for `Self` + fn as_h1_value(&self) -> Option<&str>; + /// Get parsed H2 header value `Self` + fn as_h2_value(&self) -> Option<&str>; + /// Get parsed H3 header value `Self` + fn as_h3_value(&self) -> Option<&str>; + /// Get parsed header value `Self` match `Level` + fn as_value_match_level(&self, level: Level) -> Option<&str>; + /// 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<&str> { + if let Some(value) = self.as_h1_value() { + return Some(value); + } + if let Some(value) = self.as_h2_value() { + return Some(value); + } + if let Some(value) = self.as_h3_value() { + return Some(value); + } + None + } + fn as_h1_value(&self) -> Option<&str> { + self.as_value_match_level(Level::H1) + } + fn as_h2_value(&self) -> Option<&str> { + self.as_value_match_level(Level::H2) + } + fn as_h3_value(&self) -> Option<&str> { + self.as_value_match_level(Level::H3) + } + fn as_value_match_level(&self, level: Level) -> Option<&str> { + if let Some(value) = self.strip_prefix(level.as_tag()) { + if value.trim_start().starts_with(Level::H1.as_tag()) { + return None; + } + return Some(value.trim()); + } + None + } + fn to_level(&self) -> Option { + if self.as_h1_value().is_some() { + return Some(Level::H1); + } + if self.as_h2_value().is_some() { + return Some(Level::H2); + } + if self.as_h3_value().is_some() { + return Some(Level::H3); + } + None + } + fn to_source(&self, level: &Level) -> String { + format!("{} {}", level.as_tag(), self.trim()) + } +} diff --git a/src/line/header/level.rs b/src/line/header/level.rs new file mode 100644 index 0000000..2fa001b --- /dev/null +++ b/src/line/header/level.rs @@ -0,0 +1,16 @@ +/// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) type holder +pub enum Level { + H1, + H2, + H3, +} + +impl Level { + pub fn as_tag(&self) -> &str { + match self { + Level::H1 => "#", + Level::H2 => "##", + Level::H3 => "###", + } + } +}