diff --git a/src/app/browser/window/tab/item/page/client.rs b/src/app/browser/window/tab/item/page/client.rs index 79c3cb9b..769c85aa 100644 --- a/src/app/browser/window/tab/item/page/client.rs +++ b/src/app/browser/window/tab/item/page/client.rs @@ -1,17 +1,17 @@ pub mod driver; -mod feature; +pub mod request; pub mod response; pub mod status; // Children dependencies pub use driver::Driver; -use feature::Feature; +pub use request::Request; pub use response::Response; pub use status::Status; // Global dependencies use crate::{tool::now, Profile}; -use gtk::{gio::Cancellable, prelude::CancellableExt}; +use gtk::{gio::Cancellable, glib::Priority, prelude::CancellableExt}; use std::{ cell::{Cell, RefCell}, rc::Rc, @@ -42,16 +42,15 @@ impl Client { /// Begin new request /// * the `query` as string, to support system routes (e.g. `source:` prefix) - pub fn request_async(&self, request: &str, callback: impl FnOnce(Response) + 'static) { + pub fn request_async(&self, query: &str, callback: impl FnOnce(Response) + 'static) { // Update client status self.status.replace(Status::Request { time: now(), - value: request.to_string(), + value: query.to_string(), }); - self.driver.feature_async( - Feature::from_string(request), - self.new_cancellable(), + self.driver.request_async( + Request::build(query, None, self.new_cancellable(), Priority::DEFAULT), callback, ); } diff --git a/src/app/browser/window/tab/item/page/client/driver.rs b/src/app/browser/window/tab/item/page/client/driver.rs index 5889e333..97c1ad95 100644 --- a/src/app/browser/window/tab/item/page/client/driver.rs +++ b/src/app/browser/window/tab/item/page/client/driver.rs @@ -8,12 +8,14 @@ pub mod status; pub use status::Status; // Global dependencies -use super::{feature::Request, response, response::Failure, Feature, Response}; -use crate::{tool::now, Profile}; -use gtk::{ - gio::{Cancellable, SocketClientEvent}, - prelude::SocketClientExt, +use super::{ + request::{feature::Protocol, Feature}, + response, + response::Failure, + Request, Response, }; +use crate::{tool::now, Profile}; +use gtk::{gio::SocketClientEvent, prelude::SocketClientExt}; use std::rc::Rc; pub struct Driver { @@ -61,19 +63,19 @@ impl Driver { /// Make new async `Feature` request /// * return `Response` in callback function - pub fn feature_async( - &self, - feature: Feature, - cancellable: Cancellable, - callback: impl FnOnce(Response) + 'static, - ) { - match feature { - Feature::Download { request } => match request { - Request::Gemini { uri } => gemini::request_async( + pub fn request_async(&self, request: Request, callback: impl FnOnce(Response) + 'static) { + match request.feature { + Feature::Download(protocol) => match protocol { + Protocol::Gemini { + uri, + cancellable, + priority, + } => gemini::request_async( &self.profile, &self.gemini, uri.clone(), cancellable.clone(), + priority, move |result| { callback(match result { Ok(response) => Response::Download { @@ -91,24 +93,38 @@ impl Driver { message: "Download feature yet not supported for this request".to_string(), })), // @TODO or maybe panic as unexpected }, - Feature::Default { request } => match request { - Request::Gemini { uri } => gemini::request_async( + Feature::Default(protocol) => match protocol { + Protocol::Gemini { + uri, + cancellable, + priority, + } => gemini::request_async( &self.profile, &self.gemini, uri.clone(), cancellable.clone(), - move |result| gemini::handle(result, uri, cancellable, false, callback), + priority, + move |result| { + gemini::handle(result, uri, cancellable, priority, false, callback) + }, ), - Request::Titan { .. } => todo!(), - Request::Undefined => todo!(), + Protocol::Titan { .. } => todo!(), + Protocol::Unsupported => todo!(), }, - Feature::Source { request } => match request { - Request::Gemini { uri } => gemini::request_async( + Feature::Source(protocol) => match protocol { + Protocol::Gemini { + uri, + cancellable, + priority, + } => gemini::request_async( &self.profile, &self.gemini, uri.clone(), cancellable.clone(), - move |result| gemini::handle(result, uri, cancellable, true, callback), + priority, + move |result| { + gemini::handle(result, uri, cancellable, priority, true, callback) + }, ), _ => callback(Response::Failure(Failure::Error { message: "Source view feature yet not supported for this request".to_string(), diff --git a/src/app/browser/window/tab/item/page/client/driver/gemini.rs b/src/app/browser/window/tab/item/page/client/driver/gemini.rs index 61b6ae86..772eafd9 100644 --- a/src/app/browser/window/tab/item/page/client/driver/gemini.rs +++ b/src/app/browser/window/tab/item/page/client/driver/gemini.rs @@ -14,12 +14,13 @@ pub fn request_async( client: &Rc, uri: Uri, cancellable: Cancellable, + priority: Priority, callback: impl FnOnce(Result) + 'static, ) { let request = uri.to_string(); client.request_async( ggemini::client::Request::gemini(uri), - Priority::DEFAULT, + priority, cancellable, // Search for user certificate match request // * @TODO this feature does not support multi-protocol yet @@ -40,6 +41,7 @@ pub fn handle( result: Result, base: Uri, cancellable: Cancellable, + priority: Priority, is_source_request: bool, // @TODO yet partial implementation callback: impl FnOnce(Response) + 'static, ) { @@ -66,7 +68,7 @@ pub fn handle( Some(mime) => match mime.as_str() { "text/gemini" => Text::from_stream_async( response.connection.stream(), - Priority::DEFAULT, + priority, cancellable, move |result| match result { Ok(text) => callback(Response::TextGemini { diff --git a/src/app/browser/window/tab/item/page/client/feature.rs b/src/app/browser/window/tab/item/page/client/feature.rs deleted file mode 100644 index b618e60a..00000000 --- a/src/app/browser/window/tab/item/page/client/feature.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub mod request; -pub use request::Request; - -/// Feature wrapper for client `Request` -pub enum Feature { - /// Common feature for protocol selected (e.g. browser view) - Default { request: Request }, - /// Download request with externally selected method (e.g. to file) - Download { request: Request }, - /// View request as the source (like `source-view`) - Source { request: Request }, -} - -impl Feature { - // Constructors - - /// Parse new `Self` from string - pub fn from_string(query: &str) -> Self { - if let Some(postfix) = query.strip_prefix("download:") { - return Self::Download { - request: Request::from_string(postfix), - }; - } - - if let Some(postfix) = query.strip_prefix("source:") { - return Self::Source { - request: Request::from_string(postfix), - }; - } - - Self::Default { - request: Request::from_string(query), - } - } -} diff --git a/src/app/browser/window/tab/item/page/client/feature/request.rs b/src/app/browser/window/tab/item/page/client/feature/request.rs deleted file mode 100644 index b4f101c1..00000000 --- a/src/app/browser/window/tab/item/page/client/feature/request.rs +++ /dev/null @@ -1,30 +0,0 @@ -mod search; - -// Global dependencies -use gtk::glib::{Uri, UriFlags}; - -pub enum Request { - Gemini { uri: Uri }, - Titan { uri: Uri }, - Undefined, -} - -impl Request { - // Constructors - - /// Create new `Self` from parsable request string - pub fn from_string(request: &str) -> Self { - match Uri::parse(request, UriFlags::NONE) { - Ok(uri) => match uri.scheme().as_str() { - "gemini" => Self::Gemini { uri }, - "titan" => Self::Titan { uri }, - _ => Self::Undefined, - }, - // Search request if the request could not be parsed as the valid [URI](https://docs.gtk.org/glib/struct.Uri.html) - // * @TODO implement DNS resolver lookup before assign this option - Err(_) => Self::Gemini { - uri: search::tgls(request), - }, - } - } -} diff --git a/src/app/browser/window/tab/item/page/client/feature/request/search.rs b/src/app/browser/window/tab/item/page/client/feature/request/search.rs deleted file mode 100644 index d4fd854f..00000000 --- a/src/app/browser/window/tab/item/page/client/feature/request/search.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Search providers [Uri](https://docs.gtk.org/glib/struct.Uri.html) asset - -// Global dependencies -use gtk::glib::{Uri, UriFlags}; - -/// Build TGLS [Uri](https://docs.gtk.org/glib/struct.Uri.html) -pub fn tgls(query: &str) -> Uri { - Uri::build( - UriFlags::NONE, - "gemini", - None, - Some("tlgs.one"), - 1965, - "search", - Some(&Uri::escape_string(query, None, false)), // @TODO is `escape_string` really wanted in `build` context? - None, - ) -} diff --git a/src/app/browser/window/tab/item/page/client/request.rs b/src/app/browser/window/tab/item/page/client/request.rs new file mode 100644 index 00000000..a37c527a --- /dev/null +++ b/src/app/browser/window/tab/item/page/client/request.rs @@ -0,0 +1,28 @@ +pub mod feature; +pub use feature::Feature; + +use gtk::{gio::Cancellable, glib::Priority}; + +/// Request data wrapper for `Client` +pub struct Request { + pub feature: Feature, + /// Requests chain in order to process redirection rules + pub referrer: Vec, +} + +impl Request { + // Constructors + + /// Build new `Self` + pub fn build( + query: &str, + referrer: Option>, + cancellable: Cancellable, + priority: Priority, + ) -> Self { + Self { + feature: Feature::build(query, cancellable, priority), + referrer: referrer.unwrap_or_default(), + } + } +} diff --git a/src/app/browser/window/tab/item/page/client/request/feature.rs b/src/app/browser/window/tab/item/page/client/request/feature.rs new file mode 100644 index 00000000..39f40cd2 --- /dev/null +++ b/src/app/browser/window/tab/item/page/client/request/feature.rs @@ -0,0 +1,29 @@ +pub mod protocol; +pub use protocol::Protocol; + +use gtk::{gio::Cancellable, glib::Priority}; + +/// Feature wrapper for client `Request` +pub enum Feature { + Default(Protocol), + Download(Protocol), + Source(Protocol), + // @TODO System(Action) +} + +impl Feature { + // Constructors + + /// Parse new `Self` from string + pub fn build(query: &str, cancellable: Cancellable, priority: Priority) -> Self { + if let Some(postfix) = query.strip_prefix("download:") { + return Self::Download(Protocol::build(postfix, cancellable, priority)); + } + + if let Some(postfix) = query.strip_prefix("source:") { + return Self::Source(Protocol::build(postfix, cancellable, priority)); + } + + Self::Default(Protocol::build(query, cancellable, priority)) + } +} diff --git a/src/app/browser/window/tab/item/page/client/request/feature/protocol.rs b/src/app/browser/window/tab/item/page/client/request/feature/protocol.rs new file mode 100644 index 00000000..e8b5e720 --- /dev/null +++ b/src/app/browser/window/tab/item/page/client/request/feature/protocol.rs @@ -0,0 +1,58 @@ +// Global dependencies +use gtk::{ + gio::Cancellable, + glib::{Priority, Uri, UriFlags}, +}; + +pub enum Protocol { + Gemini { + uri: Uri, + cancellable: Cancellable, + priority: Priority, + }, + Titan { + uri: Uri, + cancellable: Cancellable, + priority: Priority, + }, + Unsupported, +} + +impl Protocol { + // Constructors + + /// Create new `Self` from parsable request string + pub fn build(query: &str, cancellable: Cancellable, priority: Priority) -> Self { + match Uri::parse(query, UriFlags::NONE) { + Ok(uri) => match uri.scheme().as_str() { + "gemini" => Self::Gemini { + uri, + cancellable, + priority, + }, + "titan" => Self::Titan { + uri, + cancellable, + priority, + }, + _ => Self::Unsupported, + }, + // Search request if the request could not be parsed as the valid [URI](https://docs.gtk.org/glib/struct.Uri.html) + // * @TODO implement DNS lookup before apply this option + Err(_) => Self::Gemini { + uri: Uri::build( + UriFlags::NONE, + "gemini", + None, + Some("tlgs.one"), + 1965, + "search", + Some(&Uri::escape_string(query, None, false)), // @TODO is `escape_string` really wanted in `build` context? + None, + ), + cancellable, + priority, + }, + } + } +}