diff --git a/README.md b/README.md index 50524da..3a84f77 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,9 @@ use ggemini::client::{ fn main() -> ExitCode { Client::new().request_async( - Request::gemini( // or `Request::titan` - Uri::parse("gemini://geminiprotocol.net/", UriFlags::NONE).unwrap(), - ), + Request::Gemini { // or `Request::Titan` + uri: Uri::parse("gemini://geminiprotocol.net/", UriFlags::NONE).unwrap(), + }, Priority::DEFAULT, Cancellable::new(), None, // optional `GTlsCertificate` diff --git a/src/client/connection.rs b/src/client/connection.rs index 842c430..9af51ed 100644 --- a/src/client/connection.rs +++ b/src/client/connection.rs @@ -3,7 +3,7 @@ pub mod request; pub mod response; pub use error::Error; -pub use request::{Gemini, Request, Titan}; +pub use request::Request; pub use response::Response; // Local dependencies @@ -67,7 +67,7 @@ impl Connection { Some(&cancellable.clone()), move |result| match result { Ok(_) => match request { - Request::Gemini(..) => { + Request::Gemini { .. } => { Response::from_connection_async(self, priority, cancellable, |result| { callback(match result { Ok(response) => Ok(response), @@ -75,8 +75,8 @@ impl Connection { }) }) } - Request::Titan(this) => output_stream.write_bytes_async( - &this.data, + Request::Titan { data, .. } => output_stream.write_bytes_async( + &data, priority, Some(&cancellable.clone()), move |result| match result { diff --git a/src/client/connection/request.rs b/src/client/connection/request.rs index e72753c..46576a3 100644 --- a/src/client/connection/request.rs +++ b/src/client/connection/request.rs @@ -1,20 +1,24 @@ pub mod error; -pub mod gemini; -pub mod titan; - pub use error::Error; -pub use gemini::Gemini; -pub use titan::Titan; // Local dependencies use gio::NetworkAddress; -use glib::Uri; +use glib::{Bytes, Uri, UriHideFlags}; /// Single `Request` implementation for different protocols pub enum Request { - Gemini(Gemini), - Titan(Titan), + Gemini { + uri: Uri, + }, + Titan { + uri: Uri, + data: Bytes, + /// MIME type is optional attribute by Titan protocol specification, + /// but server MAY reject the request without `mime` value provided. + mime: Option, + token: Option, + }, } impl Request { @@ -23,16 +27,38 @@ impl Request { /// Generate header string for `Self` pub fn header(&self) -> String { match self { - Self::Gemini(ref this) => this.header(), - Self::Titan(ref this) => this.header(), + Self::Gemini { uri } => format!("{uri}\r\n"), + Self::Titan { + uri, + data, + mime, + token, + } => { + let mut header = format!( + "{};size={}", + uri.to_string_partial(UriHideFlags::QUERY), + data.len() + ); + if let Some(ref mime) = mime { + header.push_str(&format!(";mime={mime}")); + } + if let Some(ref token) = token { + header.push_str(&format!(";token={token}")); + } + if let Some(query) = uri.query() { + header.push_str(&format!("?{query}")); + } + header.push_str("\r\n"); + header + } } } /// Get reference to `Self` [Uri](https://docs.gtk.org/glib/struct.Uri.html) pub fn uri(&self) -> &Uri { match self { - Self::Gemini(ref this) => &this.uri, - Self::Titan(ref this) => &this.uri, + Self::Gemini { uri } => uri, + Self::Titan { uri, .. } => uri, } } @@ -44,3 +70,45 @@ impl Request { } } } + +#[test] +fn test_gemini_header() { + use glib::UriFlags; + + const REQUEST: &str = "gemini://geminiprotocol.net/"; + + assert_eq!( + Request::Gemini { + uri: Uri::parse(REQUEST, UriFlags::NONE).unwrap() + } + .header(), + format!("{REQUEST}\r\n") + ); +} + +#[test] +fn test_titan_header() { + use glib::UriFlags; + + const DATA: &[u8] = &[1, 2, 3]; + const MIME: &str = "plain/text"; + const TOKEN: &str = "token"; + + assert_eq!( + Request::Titan { + uri: Uri::parse( + "titan://geminiprotocol.net/raw/path?key=value", + UriFlags::NONE + ) + .unwrap(), + data: Bytes::from(DATA), + mime: Some(MIME.to_string()), + token: Some(TOKEN.to_string()) + } + .header(), + format!( + "titan://geminiprotocol.net/raw/path;size={};mime={MIME};token={TOKEN}?key=value\r\n", + DATA.len(), + ) + ); +} diff --git a/src/client/connection/request/gemini.rs b/src/client/connection/request/gemini.rs deleted file mode 100644 index 0961713..0000000 --- a/src/client/connection/request/gemini.rs +++ /dev/null @@ -1,30 +0,0 @@ -use glib::Uri; - -/// [Gemini](https://geminiprotocol.net/docs/protocol-specification.gmi) protocol enum object for `Request` -pub struct Gemini { - pub uri: Uri, -} - -impl Gemini { - // Getters - - /// Get header string for `Self` - pub fn header(&self) -> String { - format!("{}\r\n", self.uri) - } -} - -#[test] -fn header() { - use super::{super::Request, Gemini}; - use glib::UriFlags; - - const REQUEST: &str = "gemini://geminiprotocol.net/"; - assert_eq!( - Request::Gemini(Gemini { - uri: Uri::parse(REQUEST, UriFlags::NONE).unwrap() - }) - .header(), - format!("{REQUEST}\r\n") - ); -} diff --git a/src/client/connection/request/titan.rs b/src/client/connection/request/titan.rs deleted file mode 100644 index 61d5c11..0000000 --- a/src/client/connection/request/titan.rs +++ /dev/null @@ -1,63 +0,0 @@ -use glib::{Bytes, Uri, UriHideFlags}; - -/// Formatted [Titan](gemini://transjovian.org/titan/page/The%20Titan%20Specification) `Request` -pub struct Titan { - pub uri: Uri, - pub data: Bytes, - /// MIME type is optional attribute by Titan protocol specification, - /// but server MAY reject the request without `mime` value provided. - pub mime: Option, - pub token: Option, -} - -impl Titan { - // Getters - - /// Get header string for `Self` - pub fn header(&self) -> String { - let mut header = format!( - "{};size={}", - self.uri.to_string_partial(UriHideFlags::QUERY), - self.data.len() - ); - if let Some(ref mime) = self.mime { - header.push_str(&format!(";mime={mime}")); - } - if let Some(ref token) = self.token { - header.push_str(&format!(";token={token}")); - } - if let Some(query) = self.uri.query() { - header.push_str(&format!("?{query}")); - } - header.push_str("\r\n"); - header - } -} - -#[test] -fn header() { - use super::{super::Request, Titan}; - use glib::UriFlags; - - const DATA: &[u8] = &[1, 2, 3]; - const MIME: &str = "plain/text"; - const TOKEN: &str = "token"; - - assert_eq!( - Request::Titan(Titan { - uri: Uri::parse( - "titan://geminiprotocol.net/raw/path?key=value", - UriFlags::NONE - ) - .unwrap(), - data: Bytes::from(DATA), - mime: Some(MIME.to_string()), - token: Some(TOKEN.to_string()) - }) - .header(), - format!( - "titan://geminiprotocol.net/raw/path;size={};mime={MIME};token={TOKEN}?key=value\r\n", - DATA.len(), - ) - ); -}