|
|
@ -1,58 +1,52 @@ |
|
|
|
use gtk::glib::{ |
|
|
|
use gtk::glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags, Uri, UriFlags}; |
|
|
|
gformat, markup_escape_text, GString, Regex, RegexCompileFlags, RegexMatchFlags, Uri, UriFlags, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Link { |
|
|
|
pub struct Link { |
|
|
|
// alt: Option<GString>, // [optional] alternative text
|
|
|
|
pub alt: Option<GString>, |
|
|
|
// date: Option<GString>, // [optional] date @TODO store in UnixTime?
|
|
|
|
pub date: Option<GString>, // @TODO https://docs.gtk.org/glib/struct.Date.html
|
|
|
|
// external: bool, // external link indicator
|
|
|
|
pub is_external: Option<bool>, |
|
|
|
// link: GString, // original link, wanted for title tooltip
|
|
|
|
pub uri: Uri, |
|
|
|
markup: GString, // pango markup with escaped special chars
|
|
|
|
|
|
|
|
// uri: Uri, // parsed link object (currently not in use)
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Link { |
|
|
|
impl Link { |
|
|
|
// Link structure parser
|
|
|
|
pub fn from(line: &str, to_base: Option<&Uri>) -> Option<Link> { |
|
|
|
// line - gemtext subject to parse
|
|
|
|
// Define initial values
|
|
|
|
// base - Uri object, required for:
|
|
|
|
let mut alt = None; |
|
|
|
// 1. relative to absolute address conversion
|
|
|
|
let mut date = None; |
|
|
|
// 2. external links indication
|
|
|
|
let mut is_external = None; |
|
|
|
// returns new Link struct or None
|
|
|
|
|
|
|
|
pub fn from(line: &str, base: &Uri) -> Option<Link> { |
|
|
|
// Begin line parse
|
|
|
|
// Init struct members
|
|
|
|
let regex = Regex::split_simple( |
|
|
|
// let mut alt: Option<GString> = None;
|
|
|
|
|
|
|
|
// let mut date: Option<GString> = None;
|
|
|
|
|
|
|
|
let external: bool; |
|
|
|
|
|
|
|
let link: GString; |
|
|
|
|
|
|
|
let markup: GString; |
|
|
|
|
|
|
|
let uri: Uri; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Parse line
|
|
|
|
|
|
|
|
let parsed = Regex::split_simple( |
|
|
|
|
|
|
|
r"^=>\s*([^\s]+)\s*(\d{4}-\d{2}-\d{2})?\s*(.+)?$", |
|
|
|
r"^=>\s*([^\s]+)\s*(\d{4}-\d{2}-\d{2})?\s*(.+)?$", |
|
|
|
line, |
|
|
|
line, |
|
|
|
RegexCompileFlags::DEFAULT, |
|
|
|
RegexCompileFlags::DEFAULT, |
|
|
|
RegexMatchFlags::DEFAULT, |
|
|
|
RegexMatchFlags::DEFAULT, |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
// Address
|
|
|
|
// Detect address required to continue
|
|
|
|
match parsed.get(1) { |
|
|
|
let unresolved_address = regex.get(1)?; |
|
|
|
Some(address) => { |
|
|
|
|
|
|
|
// Define original link value (used in titles or when alt is empty)
|
|
|
|
// Convert address to the valid URI
|
|
|
|
link = GString::from(address.as_str()); |
|
|
|
let uri = match to_base { |
|
|
|
// Links in document usually relative, make them absolute to base given
|
|
|
|
// Base conversion requested
|
|
|
|
match Uri::resolve_relative(Some(&base.to_str()), address.as_str(), UriFlags::NONE) |
|
|
|
Some(base_uri) => { |
|
|
|
{ |
|
|
|
// Convert relative address to absolute
|
|
|
|
Ok(resolved) => { |
|
|
|
match Uri::resolve_relative( |
|
|
|
// Make URI parsed as always valid (no idea why does lib operate strings, not objects)
|
|
|
|
Some(&base_uri.to_str()), |
|
|
|
match Uri::parse(&resolved, UriFlags::NONE) { |
|
|
|
unresolved_address.as_str(), |
|
|
|
Ok(object) => { |
|
|
|
UriFlags::NONE, |
|
|
|
// Set external status
|
|
|
|
) { |
|
|
|
external = |
|
|
|
Ok(resolved_str) => { |
|
|
|
object.host() != base.host() || object.port() != base.port(); |
|
|
|
// Try convert string to the valid URI
|
|
|
|
|
|
|
|
match Uri::parse(&resolved_str, UriFlags::NONE) { |
|
|
|
// Set struct URI
|
|
|
|
Ok(resolved_uri) => { |
|
|
|
uri = object; |
|
|
|
// Change external status
|
|
|
|
|
|
|
|
is_external = Some( |
|
|
|
|
|
|
|
resolved_uri.host() != base_uri.host() |
|
|
|
|
|
|
|
|| resolved_uri.port() != base_uri.port(), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Result
|
|
|
|
|
|
|
|
resolved_uri |
|
|
|
} |
|
|
|
} |
|
|
|
Err(_) => return None, |
|
|
|
Err(_) => return None, |
|
|
|
} |
|
|
|
} |
|
|
@ -60,74 +54,31 @@ impl Link { |
|
|
|
Err(_) => return None, |
|
|
|
Err(_) => return None, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
None => return None, |
|
|
|
// Base resolve not requested
|
|
|
|
} |
|
|
|
None => { |
|
|
|
|
|
|
|
// Just try convert address to valid URI
|
|
|
|
// Create link name based on external status, date and alt values
|
|
|
|
match Uri::parse(&unresolved_address, UriFlags::NONE) { |
|
|
|
let mut name = Vec::new(); |
|
|
|
Ok(unresolved_uri) => unresolved_uri, |
|
|
|
|
|
|
|
Err(_) => return None, |
|
|
|
if external { |
|
|
|
} |
|
|
|
name.push("⇖".to_string()); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Date
|
|
|
|
// Date
|
|
|
|
if let Some(this) = parsed.get(2) { |
|
|
|
if let Some(value) = regex.get(2) { |
|
|
|
// date = Some(GString::from(this.to_string()));
|
|
|
|
date = Some(GString::from(value.as_str())) |
|
|
|
name.push(this.to_string()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Alt
|
|
|
|
// Alt
|
|
|
|
match parsed.get(3) { |
|
|
|
if let Some(value) = regex.get(3) { |
|
|
|
// Not empty
|
|
|
|
alt = Some(GString::from(value.as_str())) |
|
|
|
Some(this) => { |
|
|
|
|
|
|
|
// alt = Some(GString::from(this.to_string()));
|
|
|
|
|
|
|
|
name.push(this.to_string()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Empty, use resolved address
|
|
|
|
|
|
|
|
None => name.push(link.to_string()), |
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Markup
|
|
|
|
|
|
|
|
markup = gformat!( |
|
|
|
|
|
|
|
"<a href=\"{}\" title=\"{}\"><span underline=\"none\">{}</span></a>\n", |
|
|
|
|
|
|
|
markup_escape_text(&uri.to_str()), // use resolved address for href
|
|
|
|
|
|
|
|
markup_escape_text(&link), // show original address for title
|
|
|
|
|
|
|
|
markup_escape_text(&name.join(" ")), |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Some(Self { |
|
|
|
Some(Self { |
|
|
|
// alt,
|
|
|
|
alt, |
|
|
|
// date,
|
|
|
|
date, |
|
|
|
// external,
|
|
|
|
is_external, |
|
|
|
// link,
|
|
|
|
uri, |
|
|
|
markup, |
|
|
|
|
|
|
|
// uri,
|
|
|
|
|
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Getters
|
|
|
|
|
|
|
|
/* @TODO
|
|
|
|
|
|
|
|
pub fn alt(&self) -> &Option<GString> { |
|
|
|
|
|
|
|
&self.alt |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn date(&self) -> &Option<GString> { |
|
|
|
|
|
|
|
&self.date |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn external(&self) -> &bool { |
|
|
|
|
|
|
|
&self.external |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn link(&self) -> &GString { |
|
|
|
|
|
|
|
&self.link |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn uri(&self) -> &Uri { |
|
|
|
|
|
|
|
&self.uri |
|
|
|
|
|
|
|
}*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn markup(&self) -> &GString { |
|
|
|
|
|
|
|
&self.markup |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|