reorganize header component

This commit is contained in:
yggverse 2025-03-16 16:39:02 +02:00
parent c29f1ba529
commit 3e16995e00
3 changed files with 105 additions and 68 deletions

View file

@ -1,15 +1,8 @@
/// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) tag pub mod gemtext;
/// * store as entire static chars array pub mod level;
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 use gemtext::Gemtext;
pub enum Level { pub use level::Level;
H1,
H2,
H3,
}
/// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) entity holder /// [Header](https://geminiprotocol.net/docs/gemtext-specification.gmi#heading-lines) entity holder
pub struct Header { pub struct Header {
@ -22,10 +15,25 @@ impl Header {
/// Parse `Self` from line string /// Parse `Self` from line string
pub fn parse(line: &str) -> Option<Self> { pub fn parse(line: &str) -> Option<Self> {
Some(Self { if let Some(value) = line.as_h1_value() {
level: line.to_level()?, return Some(Self {
value: line.as_value()?.to_string(), 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 // Converters
@ -35,56 +43,3 @@ impl Header {
self.value.to_source(&self.level) 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<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<&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<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()
)
}
}

View file

@ -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<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<&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<Level> {
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())
}
}

16
src/line/header/level.rs Normal file
View file

@ -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 => "###",
}
}
}