implement file handlers

This commit is contained in:
yggverse 2025-02-13 16:03:55 +02:00
parent 93da67aa7e
commit 8a4689fc0a

View file

@ -1,5 +1,9 @@
use super::{Feature, Page}; use super::{Feature, Page};
use gtk::{gio::Cancellable, glib::Uri}; use gtk::{
gio::Cancellable,
glib::{GString, Uri},
};
use sourceview::prelude::FileExt;
use std::rc::Rc; use std::rc::Rc;
/// Local files client driver /// Local files client driver
@ -15,49 +19,148 @@ impl File {
Self { page: page.clone() } // @TODO Self { page: page.clone() } // @TODO
} }
pub fn handle(&self, uri: Uri, _feature: Rc<Feature>, cancellable: Cancellable) { pub fn handle(&self, uri: Uri, feature: Rc<Feature>, cancellable: Cancellable) {
use gtk::{ use gtk::{
gio::{File, FileQueryInfoFlags, FileType}, gio::{File, FileQueryInfoFlags, FileType},
glib::Priority, glib::Priority,
prelude::FileExtManual, prelude::FileExtManual,
}; };
// try handle as file let url = uri.to_string();
File::for_uri(&uri.to_string()).load_contents_async(Some(&cancellable), {
let cancellable = cancellable.clone(); match File::for_uri(&url).query_file_type(FileQueryInfoFlags::NONE, Some(&cancellable)) {
let uri = uri.clone(); FileType::Directory => File::for_uri(&url).enumerate_children_async(
let _page = self.page.clone(); "standard::content-type",
move |result| match result { FileQueryInfoFlags::NONE,
Ok(_) => todo!(), Priority::DEFAULT,
// is not a file, try handle as directory Some(&cancellable),
Err(_) => File::for_uri(&uri.to_string()).enumerate_children_async( |result| match result {
"standard::content-type", Ok(file_enumerator) => {
FileQueryInfoFlags::NONE, for entry in file_enumerator {
Priority::DEFAULT, match entry {
Some(&cancellable), Ok(file_info) => match file_info.file_type() {
|result| match result { FileType::Unknown => todo!(),
Ok(file_enumerator) => { FileType::Regular => todo!(),
for entry in file_enumerator { FileType::Directory => todo!(),
match entry { FileType::SymbolicLink => todo!(),
Ok(file_info) => match file_info.file_type() { FileType::Special => todo!(),
FileType::Unknown => todo!(), FileType::Shortcut => todo!(),
FileType::Regular => todo!(), FileType::Mountable => todo!(),
FileType::Directory => todo!(), _ => todo!(),
FileType::SymbolicLink => todo!(), },
FileType::Special => todo!(), Err(_) => todo!(),
FileType::Shortcut => todo!(),
FileType::Mountable => todo!(),
_ => todo!(),
},
Err(_) => todo!(),
}
} }
} }
}
Err(_) => todo!(), Err(_) => todo!(),
}, },
), ),
_ => {
if url.ends_with(".gmi") || url.ends_with(".gemini") {
if matches!(*feature, Feature::Source) {
text_source(&self.page, uri, cancellable)
} else {
text_gemini(&self.page, uri, cancellable)
}
} else if url.ends_with(".txt") {
text_plain(&self.page, uri, cancellable)
} else if !url.ends_with("/") {
text_gemini(&self.page, uri, cancellable)
} else {
status_failure(&self.page, "Unsupported content type")
}
} }
}); }
}
}
// Tools
/// Handle as `text/source`
fn text_source(page: &Rc<Page>, uri: Uri, cancellable: Cancellable) {
load_contents_async(&uri.to_string(), cancellable, {
let page = page.clone();
let uri = uri.clone();
move |data| text(page, uri, Text::Source(data))
});
}
/// Handle as `text/gemini`
fn text_gemini(page: &Rc<Page>, uri: Uri, cancellable: Cancellable) {
load_contents_async(&uri.to_string(), cancellable, {
let page = page.clone();
let uri = uri.clone();
move |data| text(page, uri, Text::Gemini(data))
});
}
/// Handle as `text/plain`
fn text_plain(page: &Rc<Page>, uri: Uri, cancellable: Cancellable) {
load_contents_async(&uri.to_string(), cancellable, {
let page = page.clone();
let uri = uri.clone();
move |data| text(page, uri, Text::Plain(data))
});
}
/// Handle as failure status page
fn status_failure(page: &Rc<Page>, message: &str) {
let status = page.content.to_status_failure();
status.set_description(Some(message));
page.set_title(&status.title());
page.set_progress(0.0);
}
fn load_contents_async(
url: &str,
cancellable: Cancellable,
on_success: impl FnOnce(String) + 'static,
) {
use gtk::prelude::FileExtManual;
gtk::gio::File::for_uri(url).load_contents_async(Some(&cancellable), {
move |result| match result {
Ok((ref buffer, _)) => match String::from_utf8(buffer.to_vec()) {
Ok(data) => on_success(data),
Err(_) => todo!(),
},
Err(_) => todo!(),
}
})
}
enum Text {
Gemini(String),
Plain(String),
Source(String),
}
/// Handle as text
fn text(page: Rc<Page>, uri: Uri, text: Text) {
let widget = match text {
Text::Gemini(data) => page.content.to_text_gemini(&uri, &data),
Text::Plain(data) => page.content.to_text_plain(&data),
Text::Source(data) => page.content.to_text_source(&data),
};
page.search.set(Some(widget.text_view));
page.set_title(&match widget.meta.title {
Some(title) => title.into(), // @TODO
None => uri_to_title(&uri),
});
page.set_progress(0.0);
page.window_action.find.simple_action.set_enabled(true);
}
/// Helper function, extract readable title from [Uri](https://docs.gtk.org/glib/struct.Uri.html)
/// * useful as common placeholder when page title could not be detected
/// * this feature may be improved and moved outside @TODO
fn uri_to_title(uri: &Uri) -> GString {
let path = uri.path();
if path.split('/').last().unwrap_or_default().is_empty() {
match uri.host() {
Some(host) => host,
None => "Untitled".into(),
}
} else {
path
} }
} }