replace static regex condition with resolvable hostname detection, add gemini/to_gemini api

This commit is contained in:
yggverse 2024-12-12 19:07:04 +02:00
parent 9411208c74
commit 325d5505d7
2 changed files with 44 additions and 35 deletions

View File

@ -26,10 +26,7 @@ use gtk::{
gdk::Texture, gdk::Texture,
gdk_pixbuf::Pixbuf, gdk_pixbuf::Pixbuf,
gio::SocketClientEvent, gio::SocketClientEvent,
glib::{ glib::{gformat, GString, Priority, Uri, UriFlags, UriHideFlags},
gformat, GString, Priority, Regex, RegexCompileFlags, RegexMatchFlags, Uri, UriFlags,
UriHideFlags,
},
prelude::{EditableExt, FileExt, SocketClientExt, WidgetExt}, prelude::{EditableExt, FileExt, SocketClientExt, WidgetExt},
}; };
use sqlite::Transaction; use sqlite::Transaction;
@ -225,41 +222,15 @@ impl Page {
} }
} }
Request::Search(ref query) => { Request::Search(ref query) => {
// Try interpret URI manually // try autocomplete scheme prefix @TODO optional resolve timeout
if Regex::match_simple( if self.navigation.request.to_gemini(500).is_none() {
r"^[^\/\s]+\.[\w]{2,}", // make search request to default provider @TODO optional
query,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
) {
// Seems request contain some host, try append default scheme
// * make sure new request conversable to valid URI
match Uri::parse(&format!("gemini://{query}"), UriFlags::NONE) {
Ok(uri) => {
// Update navigation entry
self.navigation
.request
.widget
.entry
.set_text(&uri.to_string());
// Load page (without history record)
self.load(false);
}
Err(_) => {
// @TODO any action here?
}
}
} else {
// Plain text given, make search request to default provider
self.navigation.request.widget.entry.set_text(&format!( self.navigation.request.widget.entry.set_text(&format!(
"gemini://tlgs.one/search?{}", "gemini://tlgs.one/search?{}",
Uri::escape_string(query, None, false) Uri::escape_string(query, None, false)
)); ));
// Load page (without history record)
self.load(false);
} }
self.load(true)
} }
}; };
} }

View File

@ -5,8 +5,9 @@ use widget::Widget;
use crate::app::browser::{window::tab::item::Action as TabAction, Action as BrowserAction}; use crate::app::browser::{window::tab::item::Action as TabAction, Action as BrowserAction};
use gtk::{ use gtk::{
gio::{Cancellable, NetworkAddress, Resolver},
glib::{gformat, GString, Uri, UriFlags}, glib::{gformat, GString, Uri, UriFlags},
prelude::EditableExt, prelude::{EditableExt, NetworkAddressExt, ResolverExt},
}; };
use sqlite::Transaction; use sqlite::Transaction;
use std::rc::Rc; use std::rc::Rc;
@ -98,6 +99,13 @@ impl Request {
self.widget.entry.set_text(&self.source()); self.widget.entry.set_text(&self.source());
} }
pub fn to_gemini(&self, resolver_timeout: u32) -> Option<GString> {
self.gemini(resolver_timeout).and_then(|url| {
self.widget.entry.set_text(&url);
Some(url)
})
}
// Getters // Getters
pub fn uri(&self) -> Option<Uri> { pub fn uri(&self) -> Option<Uri> {
@ -118,6 +126,14 @@ impl Request {
text = postfix.into() text = postfix.into()
}; };
if let Some(postfix) = text.strip_prefix("file://") {
text = postfix.into()
};
if let Some(postfix) = text.strip_prefix("gemini://") {
text = postfix.into()
};
text text
} }
@ -128,6 +144,28 @@ impl Request {
pub fn source(&self) -> GString { pub fn source(&self) -> GString {
gformat!("source:{}", self.strip_prefix()) gformat!("source:{}", self.strip_prefix())
} }
pub fn gemini(&self, resolver_timeout: u32) -> Option<GString> {
// suggest scheme
let url = gformat!("gemini://{}", self.strip_prefix().trim());
// setup default resolver
// * wanted to detect value contain **resolvable** hostname
let resolver = Resolver::default();
resolver.set_timeout(resolver_timeout);
// is connectable
if let Ok(connectable) = NetworkAddress::parse_uri(&url, 1965) {
// is resolvable @TODO async
if resolver
.lookup_by_name(&connectable.hostname(), Cancellable::NONE)
.is_ok()
{
return Some(url);
}
}
None
}
} }
// Tools // Tools