mirror of
https://github.com/YGGverse/ggemtext.git
synced 2026-04-02 01:55:36 +00:00
96 lines
3.2 KiB
Rust
96 lines
3.2 KiB
Rust
use glib::{DateTime, Regex, RegexCompileFlags, RegexMatchFlags, TimeZone, Uri, UriFlags};
|
|
|
|
/// [Link](https://geminiprotocol.net/docs/gemtext-specification.gmi#link-lines) entity holder
|
|
pub struct Link {
|
|
pub alt: Option<String>, // [optional] alternative link description
|
|
pub timestamp: Option<DateTime>, // [optional] valid link DateTime object
|
|
pub uri: Uri, // [required] valid link URI object
|
|
}
|
|
|
|
impl Link {
|
|
// Constructors
|
|
|
|
/// Parse `Self` from line string
|
|
pub fn from(line: &str, base: Option<&Uri>, timezone: Option<&TimeZone>) -> Option<Self> {
|
|
// Define initial values
|
|
let mut alt = None;
|
|
let mut timestamp = None;
|
|
|
|
// Begin line parse
|
|
let regex = Regex::split_simple(
|
|
r"^=>\s*([^\s]+)\s*(\d{4}-\d{2}-\d{2})?\s*(.+)?$",
|
|
line,
|
|
RegexCompileFlags::DEFAULT,
|
|
RegexMatchFlags::DEFAULT,
|
|
);
|
|
|
|
// Detect address required to continue
|
|
let mut unresolved_address = regex.get(1)?.to_string();
|
|
|
|
// Relative scheme patch
|
|
// https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
|
|
if let Some(p) = unresolved_address.strip_prefix("//") {
|
|
let b = base?;
|
|
let postfix = p.trim_start_matches(":");
|
|
unresolved_address = format!(
|
|
"{}://{}",
|
|
b.scheme(),
|
|
if postfix.is_empty() {
|
|
format!("{}/", b.host()?)
|
|
} else {
|
|
postfix.into()
|
|
}
|
|
)
|
|
}
|
|
// Convert address to the valid URI
|
|
let uri = match base {
|
|
// Base conversion requested
|
|
Some(base_uri) => {
|
|
// Convert relative address to absolute
|
|
match Uri::resolve_relative(
|
|
Some(&base_uri.to_str()),
|
|
unresolved_address.as_str(),
|
|
UriFlags::NONE,
|
|
) {
|
|
Ok(resolved_str) => {
|
|
// Try convert string to the valid URI
|
|
match Uri::parse(&resolved_str, UriFlags::NONE) {
|
|
Ok(resolved_uri) => resolved_uri,
|
|
Err(_) => return None,
|
|
}
|
|
}
|
|
Err(_) => return None,
|
|
}
|
|
}
|
|
// Base resolve not requested
|
|
None => {
|
|
// Try convert address to valid URI
|
|
match Uri::parse(&unresolved_address, UriFlags::NONE) {
|
|
Ok(unresolved_uri) => unresolved_uri,
|
|
Err(_) => return None,
|
|
}
|
|
}
|
|
};
|
|
|
|
// Timestamp
|
|
if let Some(date) = regex.get(2) {
|
|
timestamp = match DateTime::from_iso8601(&format!("{date}T00:00:00"), timezone) {
|
|
Ok(value) => Some(value),
|
|
Err(_) => None,
|
|
}
|
|
}
|
|
|
|
// Alt
|
|
if let Some(value) = regex.get(3) {
|
|
if !value.is_empty() {
|
|
alt = Some(value.to_string())
|
|
}
|
|
};
|
|
|
|
Some(Self {
|
|
alt,
|
|
timestamp,
|
|
uri,
|
|
})
|
|
}
|
|
}
|