From 678f906f48b5060d07123b1939ab39ca0fbbaf31 Mon Sep 17 00:00:00 2001 From: yggverse Date: Fri, 21 Mar 2025 17:02:50 +0200 Subject: [PATCH] remove unspecified inline code tag support --- src/line.rs | 1 + src/line/code.rs | 93 ++++++++++++++++++++++++-- src/line/code/{multiline => }/error.rs | 0 src/line/code/inline.rs | 46 ------------- src/line/code/inline/gemtext.rs | 38 ----------- src/line/code/multiline.rs | 59 ---------------- tests/integration.gmi | 2 - tests/integration.rs | 46 +++++-------- 8 files changed, 104 insertions(+), 181 deletions(-) rename src/line/code/{multiline => }/error.rs (100%) delete mode 100644 src/line/code/inline.rs delete mode 100644 src/line/code/inline/gemtext.rs delete mode 100644 src/line/code/multiline.rs diff --git a/src/line.rs b/src/line.rs index 3b1a1fb..2cf472a 100644 --- a/src/line.rs +++ b/src/line.rs @@ -4,6 +4,7 @@ pub mod link; pub mod list; pub mod quote; +pub use code::Code; pub use header::Header; pub use link::Link; pub use list::List; diff --git a/src/line/code.rs b/src/line/code.rs index 60345bb..aa410b5 100644 --- a/src/line/code.rs +++ b/src/line/code.rs @@ -1,7 +1,90 @@ -pub mod inline; -pub mod multiline; - -pub use inline::Inline; -pub use multiline::Multiline; +pub mod error; +pub use error::Error; pub const TAG: &str = "```"; +pub const NEW_LINE: char = '\n'; + +/// Multi-line [preformatted](https://geminiprotocol.net/docs/gemtext-specification.gmi#in-pre-formatted-mode) entity holder +pub struct Code { + pub alt: Option, + pub value: String, + pub is_completed: bool, +} + +impl Code { + // Constructors + + /// Search in line string for tag open, + /// return Self constructed on success or None + pub fn begin_from(line: &str) -> Option { + if line.starts_with(TAG) { + let alt = line.trim_start_matches(TAG).trim(); + + return Some(Self { + alt: match alt.is_empty() { + true => None, + false => Some(alt.to_string()), + }, + value: String::new(), + is_completed: false, + }); + } + None + } + + /// Continue preformatted buffer from line string, + /// set `completed` as True on close tag found + pub fn continue_from(&mut self, line: &str) -> Result<(), Error> { + // Make sure buffer not completed yet + if self.is_completed { + return Err(Error::Completed); + } + + // Append to value, trim close tag on exists + self.value.push_str(line.trim_end_matches(TAG)); + + // Line contain close tag + if line.ends_with(TAG) { + self.is_completed = true; + } else { + self.value.push(NEW_LINE); + } + + Ok(()) + } + + // Converters + + /// Convert `Self` to [Gemtext](https://geminiprotocol.net/docs/gemtext-specification.gmi) format + pub fn to_source(&self) -> String { + format!( + "{TAG}{}{NEW_LINE}{}{TAG}", + match &self.alt { + Some(alt) => format!(" {}", alt.trim()), + None => String::new(), + }, + self.value + ) + } +} + +#[test] +fn test() { + match Code::begin_from("```alt") { + Some(mut code) => { + assert!(code.continue_from("line 1").is_ok()); + assert!(code.continue_from("line 2").is_ok()); + assert!(code.continue_from("```").is_ok()); // complete + + assert!(code.is_completed); + assert_eq!(code.alt, Some("alt".into())); + assert_eq!(code.value.len(), 12 + 2); // +NL + + assert_eq!( + code.to_source(), + format!("{TAG} alt{NEW_LINE}line 1{NEW_LINE}line 2{NEW_LINE}{TAG}") + ) + } + None => assert!(false), + } +} diff --git a/src/line/code/multiline/error.rs b/src/line/code/error.rs similarity index 100% rename from src/line/code/multiline/error.rs rename to src/line/code/error.rs diff --git a/src/line/code/inline.rs b/src/line/code/inline.rs deleted file mode 100644 index a3cbc01..0000000 --- a/src/line/code/inline.rs +++ /dev/null @@ -1,46 +0,0 @@ -pub mod gemtext; -pub use gemtext::Gemtext; - -use super::TAG; - -/// Inline [preformatted](https://geminiprotocol.net/docs/gemtext-specification.gmi#in-pre-formatted-mode) entity holder -pub struct Inline { - pub value: String, -} - -impl Inline { - // Constructors - - /// Parse `Self` from line string - pub fn parse(line: &str) -> Option { - line.as_value().map(|v| Self { - value: v.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()); -} diff --git a/src/line/code/inline/gemtext.rs b/src/line/code/inline/gemtext.rs deleted file mode 100644 index ba53570..0000000 --- a/src/line/code/inline/gemtext.rs +++ /dev/null @@ -1,38 +0,0 @@ -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()); -} diff --git a/src/line/code/multiline.rs b/src/line/code/multiline.rs deleted file mode 100644 index 2e1ef32..0000000 --- a/src/line/code/multiline.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::TAG; - -pub mod error; -pub use error::Error; - -// Shared defaults - -pub const NEW_LINE: char = '\n'; - -/// Multi-line [preformatted](https://geminiprotocol.net/docs/gemtext-specification.gmi#in-pre-formatted-mode) entity holder -pub struct Multiline { - pub alt: Option, - pub value: String, - pub completed: bool, -} - -impl Multiline { - // Constructors - - /// Search in line string for tag open, - /// return Self constructed on success or None - pub fn begin_from(line: &str) -> Option { - if line.starts_with(TAG) { - let alt = line.trim_start_matches(TAG).trim(); - - return Some(Self { - alt: match alt.is_empty() { - true => None, - false => Some(alt.to_string()), - }, - value: String::new(), - completed: false, - }); - } - - None - } - - /// Continue preformatted buffer from line string, - /// set `completed` as True on close tag found - pub fn continue_from(&mut self, line: &str) -> Result<(), Error> { - // Make sure buffer not completed yet - if self.completed { - return Err(Error::Completed); - } - - // Append to value, trim close tag on exists - self.value.push_str(line.trim_end_matches(TAG)); - - // Line contain close tag - if line.ends_with(TAG) { - self.completed = true; - } else { - self.value.push(NEW_LINE); - } - - Ok(()) - } -} diff --git a/tests/integration.gmi b/tests/integration.gmi index a33000f..400695a 100644 --- a/tests/integration.gmi +++ b/tests/integration.gmi @@ -15,8 +15,6 @@ * Listing item 1 * Listing item 2 -```inline code``` - ``` alt text multi preformatted line diff --git a/tests/integration.rs b/tests/integration.rs index faf24ff..d96ccff 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,9 +1,6 @@ use ggemtext::line::{ - code::{Inline, Multiline}, + Code, Link, List, Quote, header::{Header, Level}, - link::Link, - list::List, - quote::Quote, }; use glib::{TimeZone, Uri, UriFlags}; @@ -14,15 +11,14 @@ fn gemtext() { match fs::read_to_string("tests/integration.gmi") { Ok(gemtext) => { // Init tags collection - let mut code_inline: Vec = Vec::new(); - let mut code_multiline: Vec = Vec::new(); + let mut code: Vec = Vec::new(); let mut headers: Vec
= Vec::new(); let mut links: Vec = Vec::new(); let mut list: Vec = Vec::new(); let mut quote: Vec = Vec::new(); // Define preformatted buffer - let mut code_multiline_buffer: Option = None; + let mut code_buffer: Option = None; // Define base URI as integration.gmi contain one relative link let base = Uri::parse("gemini://geminiprotocol.net", UriFlags::NONE).unwrap(); @@ -32,25 +28,18 @@ fn gemtext() { // Parse document by line for line in gemtext.lines() { - // Inline code - if let Some(result) = Inline::parse(line) { - code_inline.push(result); - continue; - } - - // Multiline code - match code_multiline_buffer { + match code_buffer { None => { - if let Some(code) = Multiline::begin_from(line) { - code_multiline_buffer = Some(code); + if let Some(code) = Code::begin_from(line) { + code_buffer = Some(code); continue; } } - Some(ref mut result) => { - assert!(Multiline::continue_from(result, line).is_ok()); - if result.completed { - code_multiline.push(code_multiline_buffer.take().unwrap()); - code_multiline_buffer = None; + Some(ref mut c) => { + assert!(c.continue_from(line).is_ok()); + if c.is_completed { + code.push(code_buffer.take().unwrap()); + code_buffer = None; } continue; } @@ -81,15 +70,10 @@ fn gemtext() { } } - // Validate inline code - assert_eq!(code_inline.len(), 1); - assert_eq!(code_inline.first().unwrap().value, "inline code"); - - // Validate multiline code - assert_eq!(code_multiline.len(), 2); - + // Validate code + assert_eq!(code.len(), 2); { - let item = code_multiline.first().unwrap(); + let item = code.first().unwrap(); assert_eq!(item.alt.clone().unwrap(), "alt text"); assert_eq!(item.value.lines().count(), 2); @@ -100,7 +84,7 @@ fn gemtext() { } // #1 { - let item = code_multiline.get(1).unwrap(); + let item = code.get(1).unwrap(); assert_eq!(item.alt.clone(), None); assert_eq!(item.value.lines().count(), 2);