remove regex dependency, rename constructor, implement zero-copy trait

This commit is contained in:
yggverse 2025-03-17 02:09:52 +02:00
parent 7f3ea670f1
commit 96f7d648b6
3 changed files with 71 additions and 20 deletions

View file

@ -1,5 +1,7 @@
pub mod gemtext;
pub use gemtext::Gemtext;
use super::TAG; use super::TAG;
use glib::{Regex, RegexCompileFlags, RegexMatchFlags};
/// Inline [preformatted](https://geminiprotocol.net/docs/gemtext-specification.gmi#in-pre-formatted-mode) entity holder /// Inline [preformatted](https://geminiprotocol.net/docs/gemtext-specification.gmi#in-pre-formatted-mode) entity holder
pub struct Inline { pub struct Inline {
@ -10,24 +12,35 @@ impl Inline {
// Constructors // Constructors
/// Parse `Self` from line string /// Parse `Self` from line string
pub fn from(line: &str) -> Option<Self> { pub fn parse(line: &str) -> Option<Self> {
// Skip next operations on prefix and postfix mismatch `TAG` line.as_value().map(|v| Self {
// * replace regex implementation @TODO value: v.to_string(),
if !line.starts_with(TAG) && !line.ends_with(TAG) {
return None;
}
// Parse line
let regex = Regex::split_simple(
r"^`{3}([^`]+)`{3}$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Extract formatted value
Some(Self {
value: regex.get(1)?.trim().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()
}
}
#[test]
fn test() {
fn assert(source: &str, value: &str) {
let list = Inline::parse(source).unwrap();
assert_eq!(list.value, value);
assert_eq!(list.to_source(), format!("```{value}```"));
}
assert("```inline```", "inline");
assert("```inline ```", "inline");
assert("``` inline ```", "inline");
assert("``` inline```", "inline");
assert("``` inline``` ", "inline");
assert("``````inline``` ", "```inline");
assert("``````inline`````` ", "```inline```");
assert("```inline`````` ", "inline```");
assert!("```inline".as_value().is_none());
assert!("```inline``` ne".as_value().is_none());
} }

View file

@ -0,0 +1,38 @@
use super::TAG;
pub trait Gemtext {
/// Get [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) value for `Self`
fn as_value(&self) -> Option<&str>;
/// Convert `Self` to [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) line
fn to_source(&self) -> String;
}
impl Gemtext for str {
fn as_value(&self) -> Option<&str> {
if let Some(p) = self.strip_prefix(TAG) {
return p.trim().strip_suffix(TAG).map(|s| s.trim());
}
None
}
fn to_source(&self) -> String {
format!("{TAG}{}{TAG}", self.trim())
}
}
#[test]
fn test() {
fn assert(source: &str, value: &str) {
assert_eq!(source.as_value(), Some(value));
assert_eq!(value.to_source(), format!("```{value}```"));
}
assert("```inline```", "inline");
assert("```inline ```", "inline");
assert("``` inline ```", "inline");
assert("``` inline```", "inline");
assert("``` inline``` ", "inline");
assert("``````inline``` ", "```inline");
assert("``````inline`````` ", "```inline```");
assert("```inline`````` ", "inline```");
assert!("```inline".as_value().is_none());
assert!("```inline``` ne".as_value().is_none());
}

View file

@ -36,7 +36,7 @@ fn gemtext() {
// Parse document by line // Parse document by line
for line in gemtext.lines() { for line in gemtext.lines() {
// Inline code // Inline code
if let Some(result) = Inline::from(line) { if let Some(result) = Inline::parse(line) {
code_inline.push(result); code_inline.push(result);
continue; continue;
} }