reorganize client Request

This commit is contained in:
yggverse 2025-01-18 05:35:39 +02:00
parent 9d690d3137
commit 4665a7ff6a
9 changed files with 164 additions and 115 deletions

View file

@ -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,
);
}

View file

@ -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(),

View file

@ -14,12 +14,13 @@ pub fn request_async(
client: &Rc<ggemini::Client>,
uri: Uri,
cancellable: Cancellable,
priority: Priority,
callback: impl FnOnce(Result<ggemini::client::Response, ggemini::client::Error>) + '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<ggemini::client::connection::Response, ggemini::client::Error>,
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 {

View file

@ -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),
}
}
}

View file

@ -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),
},
}
}
}

View file

@ -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,
)
}

View file

@ -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<Request>,
}
impl Request {
// Constructors
/// Build new `Self`
pub fn build(
query: &str,
referrer: Option<Vec<Request>>,
cancellable: Cancellable,
priority: Priority,
) -> Self {
Self {
feature: Feature::build(query, cancellable, priority),
referrer: referrer.unwrap_or_default(),
}
}
}

View file

@ -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))
}
}

View file

@ -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,
},
}
}
}