From 9152528790910f975f026b43663a288abe1e55fa Mon Sep 17 00:00:00 2001 From: yggverse Date: Mon, 28 Oct 2024 02:27:30 +0200 Subject: [PATCH] draft new api version --- README.md | 20 +-- src/client.rs | 6 - src/client/buffer.rs | 164 ------------------ src/client/buffer/error.rs | 4 - src/client/error.rs | 6 - src/client/response.rs | 43 ----- src/client/response/body.rs | 187 ++++++++++++++++++--- src/client/response/body/error.rs | 3 +- src/client/response/error.rs | 4 - src/client/response/header.rs | 140 +++++++++++---- src/client/response/header/error.rs | 4 +- src/client/response/header/meta.rs | 14 +- src/client/response/header/meta/error.rs | 1 + src/client/response/header/mime.rs | 9 +- src/client/response/header/mime/error.rs | 1 + src/client/response/header/status.rs | 9 +- src/client/response/header/status/error.rs | 1 + 17 files changed, 299 insertions(+), 317 deletions(-) delete mode 100644 src/client/buffer.rs delete mode 100644 src/client/buffer/error.rs delete mode 100644 src/client/error.rs delete mode 100644 src/client/response/error.rs diff --git a/README.md b/README.md index 026efb5..0443f1e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ # ggemini -Glib/Gio-oriented network library for [Gemini protocol](https://geminiprotocol.net/) +Glib-oriented client for [Gemini protocol](https://geminiprotocol.net/) > [!IMPORTANT] > Project in development! > -GGemini (or G-Gemini) initially created as the client extension for [Yoda Browser](https://github.com/YGGverse/Yoda), -also could be useful for any other integration as depends of -[glib](https://crates.io/crates/glib) and [gio](https://crates.io/crates/gio) (`v2_66`) crates only. +This library mostly written as the network extension for [Yoda](https://github.com/YGGverse/Yoda) - GTK Browser for Gemini Protocol, +it also could be useful for any other integrations as depend of [glib](https://crates.io/crates/glib) and [gio](https://crates.io/crates/gio) (`2.66+`) crates only. ## Install @@ -20,23 +19,16 @@ cargo add ggemini ### `client` -Gio API already includes powerful [SocketClient](https://docs.gtk.org/gio/class.SocketClient.html), -`ggemini::client` just extends some features a bit, to simplify interaction with socket over Gemini Protocol. - -It also contain some children components/mods bellow for low-level access any feature directly. - -#### `client::buffer` +[Gio](https://docs.gtk.org/gio/) API already provide powerful [SocketClient](https://docs.gtk.org/gio/class.SocketClient.html). +This library just extend some minimal features wanted for Gemini Protocol #### `client::response` -Response parser for [InputStream](https://docs.gtk.org/gio/class.InputStream.html) +Response parser, currently includes low-level interaction API for [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) -#### `client::response::Response` #### `client::response::header` #### `client::response::body` -https://docs.gtk.org/glib/struct.Bytes.html - ## See also * [ggemtext](https://github.com/YGGverse/ggemtext) - Glib-oriented Gemtext API \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index 2be3ea9..4c6f2cd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1 @@ -pub mod buffer; -pub mod error; pub mod response; - -pub use buffer::Buffer; -pub use error::Error; -pub use response::Response; diff --git a/src/client/buffer.rs b/src/client/buffer.rs deleted file mode 100644 index f3e8d50..0000000 --- a/src/client/buffer.rs +++ /dev/null @@ -1,164 +0,0 @@ -pub mod error; -pub use error::Error; - -use gio::{ - prelude::{IOStreamExt, InputStreamExt}, - Cancellable, SocketConnection, -}; -use glib::{Bytes, Priority}; - -pub const DEFAULT_CAPACITY: usize = 0x400; -pub const DEFAULT_MAX_SIZE: usize = 0xfffff; - -/// Dynamically allocated [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) buffer -/// with configurable `capacity` and `max_size` limits -pub struct Buffer { - buffer: Vec, - max_size: usize, -} - -impl Buffer { - // Constructors - - /// Create new `Self` with default `capacity` and `max_size` preset - pub fn new() -> Self { - Self::new_with_options(Some(DEFAULT_CAPACITY), Some(DEFAULT_MAX_SIZE)) - } - - /// Create new `Self` with options - /// - /// Options: - /// * `capacity` initial bytes request to reduce extra memory reallocation (`DEFAULT_CAPACITY` if `None`) - /// * `max_size` max bytes to prevent memory overflow by unknown stream source (`DEFAULT_MAX_SIZE` if `None`) - pub fn new_with_options(capacity: Option, max_size: Option) -> Self { - Self { - buffer: Vec::with_capacity(match capacity { - Some(value) => value, - None => DEFAULT_CAPACITY, - }), - max_size: match max_size { - Some(value) => value, - None => DEFAULT_MAX_SIZE, - }, - } - } - - // Intentable constructors - - /// Simplest way to create `Self` buffer from [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) - /// - /// Options: - /// * `connection` - [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) to read bytes from - /// * `callback` function to apply on all async operations complete, return `Result)>` - pub fn from_connection_async( - connection: SocketConnection, - callback: impl FnOnce(Result)>) + 'static, - ) { - Self::read_all_async(Self::new(), connection, None, None, None, callback); - } - - // Actions - - /// Asynchronously read all [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) - /// from [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) to `Self.buffer` - /// - /// Useful to grab entire stream without risk of memory overflow (according to `Self.max_size`), - /// reduce extra memory reallocations by `capacity` option. - /// - /// **Notes** - /// - /// We are using entire [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) reference - /// instead of [InputStream](https://docs.gtk.org/gio/class.InputStream.html) directly just to keep main connection alive in the async context - /// - /// **Options** - /// * `connection` - [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) to read bytes from - /// * `cancellable` - [Cancellable](https://docs.gtk.org/gio/class.Cancellable.html) or `None::<&Cancellable>` by default - /// * `priority` - [Priority::DEFAULT](https://docs.gtk.org/glib/const.PRIORITY_DEFAULT.html) by default - /// * `chunk` optional bytes count to read per chunk (`0x100` by default) - /// * `callback` function to apply on all async operations complete, return `Result)>` - pub fn read_all_async( - mut self, - connection: SocketConnection, - cancelable: Option, - priority: Option, - chunk: Option, - callback: impl FnOnce(Result)>) + 'static, - ) { - connection.input_stream().read_bytes_async( - match chunk { - Some(value) => value, - None => 0x100, - }, - match priority { - Some(value) => value, - None => Priority::DEFAULT, - }, - match cancelable.clone() { - Some(value) => Some(value), - None => None::, - } - .as_ref(), - move |result| match result { - Ok(bytes) => { - // No bytes were read, end of stream - if bytes.len() == 0 { - return callback(Ok(self)); - } - - // Save chunk to buffer - if let Err(reason) = self.push(bytes) { - return callback(Err((reason, None))); - }; - - // Continue bytes read.. - self.read_all_async(connection, cancelable, priority, chunk, callback); - } - Err(reason) => callback(Err((Error::InputStream, Some(reason.message())))), - }, - ); - } - - /// Push [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) to `Self.buffer` - /// - /// Return `Error::Overflow` on `max_size` reached - pub fn push(&mut self, bytes: Bytes) -> Result { - // Calculate new size value - let total = self.buffer.len() + bytes.len(); - - // Validate overflow - if total > self.max_size { - return Err(Error::Overflow); - } - - // Success - self.buffer.push(bytes); - - Ok(total) - } - - // Setters - - /// Set new `max_size` value, `DEFAULT_MAX_SIZE` if `None` - pub fn set_max_size(&mut self, value: Option) { - self.max_size = match value { - Some(size) => size, - None => DEFAULT_MAX_SIZE, - } - } - - // Getters - - /// Get reference to bytes collected - pub fn buffer(&self) -> &Vec { - &self.buffer - } - - /// Return copy of bytes as UTF-8 vector - pub fn to_utf8(&self) -> Vec { - self.buffer - .iter() - .flat_map(|byte| byte.iter()) - .cloned() - .collect() - } -} diff --git a/src/client/buffer/error.rs b/src/client/buffer/error.rs deleted file mode 100644 index 82c93e6..0000000 --- a/src/client/buffer/error.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub enum Error { - InputStream, - Overflow, -} diff --git a/src/client/error.rs b/src/client/error.rs deleted file mode 100644 index 10cda29..0000000 --- a/src/client/error.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub enum Error { - Close, - Connection, - Request, - Response, -} diff --git a/src/client/response.rs b/src/client/response.rs index 866b4e3..a6016ba 100644 --- a/src/client/response.rs +++ b/src/client/response.rs @@ -1,48 +1,5 @@ pub mod body; -pub mod error; pub mod header; pub use body::Body; -pub use error::Error; pub use header::Header; - -use glib::Bytes; - -pub struct Response { - header: Header, - body: Body, -} - -impl Response { - /// Create new `Self` - pub fn new(header: Header, body: Body) -> Self { - Self { header, body } - } - - /// Construct from [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) - /// - /// Useful for [Gio::InputStream](https://docs.gtk.org/gio/class.InputStream.html): - /// * [read_bytes](https://docs.gtk.org/gio/method.InputStream.read_bytes.html) - /// * [read_bytes_async](https://docs.gtk.org/gio/method.InputStream.read_bytes_async.html) - pub fn from(bytes: &Bytes) -> Result { - let header = match Header::from_response(bytes) { - Ok(result) => result, - Err(_) => return Err(Error::Header), - }; - - let body = match Body::from_response(bytes) { - Ok(result) => result, - Err(_) => return Err(Error::Body), - }; - - Ok(Self::new(header, body)) - } - - pub fn header(&self) -> &Header { - &self.header - } - - pub fn body(&self) -> &Body { - &self.body - } -} diff --git a/src/client/response/body.rs b/src/client/response/body.rs index 80d17b5..5d59124 100644 --- a/src/client/response/body.rs +++ b/src/client/response/body.rs @@ -1,46 +1,187 @@ pub mod error; pub use error::Error; -use glib::{Bytes, GString}; +use gio::{ + prelude::{IOStreamExt, InputStreamExt}, + Cancellable, SocketConnection, +}; +use glib::{Bytes, GString, Priority}; +pub const DEFAULT_CAPACITY: usize = 0x400; +pub const DEFAULT_MAX_SIZE: usize = 0xfffff; + +/// Body container with memory-overflow-safe, dynamically allocated [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) buffer +/// +/// **Features** +/// +/// * configurable `capacity` and `max_size` options +/// * build-in [InputStream](https://docs.gtk.org/gio/class.InputStream.html) parser +/// +/// **Notice** +/// +/// * Recommended for gemtext documents +/// * For media types, use native stream processors (e.g. [Pixbuf](https://docs.gtk.org/gdk-pixbuf/ctor.Pixbuf.new_from_stream.html) for images) pub struct Body { - buffer: Vec, + buffer: Vec, + max_size: usize, } impl Body { // Constructors - pub fn from_response(bytes: &Bytes) -> Result { - let start = Self::start(bytes)?; - let buffer = match bytes.get(start..) { - Some(result) => result, - None => return Err(Error::Buffer), - }; + /// Create new empty `Self` with default `capacity` and `max_size` preset + pub fn new() -> Self { + Self::new_with_options(Some(DEFAULT_CAPACITY), Some(DEFAULT_MAX_SIZE)) + } - Ok(Self { - buffer: Vec::from(buffer), - }) + /// Create new new `Self` with options + /// + /// Options: + /// * `capacity` initial bytes request to reduce extra memory reallocation (`DEFAULT_CAPACITY` if `None`) + /// * `max_size` max bytes to prevent memory overflow by unknown stream source (`DEFAULT_MAX_SIZE` if `None`) + pub fn new_with_options(capacity: Option, max_size: Option) -> Self { + Self { + buffer: Vec::with_capacity(match capacity { + Some(value) => value, + None => DEFAULT_CAPACITY, + }), + max_size: match max_size { + Some(value) => value, + None => DEFAULT_MAX_SIZE, + }, + } + } + + /// Simple way to create `Self` buffer from active [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) + /// + /// **Options** + /// * `connection` - [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) to read bytes from + /// * `callback` function to apply on async operations complete, return `Result)>` + /// + /// **Notes** + /// + /// * method requires entire [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html), + /// not just [InputStream](https://docs.gtk.org/gio/class.InputStream.html) because of async features; + /// * use this method after `Header` bytes taken from input stream connected (otherwise, take a look on high-level `Response` parser) + pub fn from_socket_connection_async( + connection: SocketConnection, + callback: impl FnOnce(Result)>) + 'static, + ) { + Self::read_all_async(Self::new(), connection, None, None, None, callback); + } + + // Actions + + /// Asynchronously read all [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) + /// from [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) to `Self.buffer` by `chunk` + /// + /// Useful to grab entire stream without risk of memory overflow (according to `Self.max_size`), + /// reduce extra memory reallocations by `capacity` option. + /// + /// **Notes** + /// + /// We are using entire [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) reference + /// instead of [InputStream](https://docs.gtk.org/gio/class.InputStream.html) just to keep main connection alive in the async chunks context + /// + /// **Options** + /// * `connection` - [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html) to read bytes from + /// * `cancellable` - [Cancellable](https://docs.gtk.org/gio/class.Cancellable.html) or `None::<&Cancellable>` by default + /// * `priority` - [Priority::DEFAULT](https://docs.gtk.org/glib/const.PRIORITY_DEFAULT.html) by default + /// * `chunk` optional bytes count to read per chunk (`0x100` by default) + /// * `callback` function to apply on all async operations complete, return `Result)>` + pub fn read_all_async( + mut self, + connection: SocketConnection, + cancelable: Option, + priority: Option, + chunk: Option, + callback: impl FnOnce(Result)>) + 'static, + ) { + connection.input_stream().read_bytes_async( + match chunk { + Some(value) => value, + None => 0x100, + }, + match priority { + Some(value) => value, + None => Priority::DEFAULT, + }, + match cancelable.clone() { + Some(value) => Some(value), + None => None::, + } + .as_ref(), + move |result| match result { + Ok(bytes) => { + // No bytes were read, end of stream + if bytes.len() == 0 { + return callback(Ok(self)); + } + + // Save chunk to buffer + if let Err(reason) = self.push(bytes) { + return callback(Err((reason, None))); + }; + + // Continue bytes read.. + self.read_all_async(connection, cancelable, priority, chunk, callback); + } + Err(reason) => callback(Err((Error::InputStream, Some(reason.message())))), + }, + ); + } + + /// Push [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) to `Self.buffer` + /// + /// Return `Error::Overflow` on `max_size` reached + pub fn push(&mut self, bytes: Bytes) -> Result { + // Calculate new size value + let total = self.buffer.len() + bytes.len(); + + // Validate overflow + if total > self.max_size { + return Err(Error::Overflow); + } + + // Success + self.buffer.push(bytes); + + Ok(total) + } + + // Setters + + /// Set new `max_size` value, `DEFAULT_MAX_SIZE` if `None` + pub fn set_max_size(&mut self, value: Option) { + self.max_size = match value { + Some(size) => size, + None => DEFAULT_MAX_SIZE, + } } // Getters - pub fn buffer(&self) -> &[u8] { + + /// Get reference to `Self.buffer` [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) collected + pub fn buffer(&self) -> &Vec { &self.buffer } + /// Return copy of `Self.buffer` [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) as UTF-8 vector + pub fn to_utf8(&self) -> Vec { + self.buffer + .iter() + .flat_map(|byte| byte.iter()) + .cloned() + .collect() + } + + // Intentable getters + + /// Try convert `Self.buffer` [Bytes](https://docs.gtk.org/glib/struct.Bytes.html) to GString pub fn to_gstring(&self) -> Result { - match GString::from_utf8(self.buffer.to_vec()) { + match GString::from_utf8(self.to_utf8()) { Ok(result) => Ok(result), Err(_) => Err(Error::Decode), } } - - // Tools - fn start(buffer: &[u8]) -> Result { - for (offset, &byte) in buffer.iter().enumerate() { - if byte == b'\n' { - return Ok(offset + 1); - } - } - Err(Error::Format) - } } diff --git a/src/client/response/body/error.rs b/src/client/response/body/error.rs index 3284dea..69a71fc 100644 --- a/src/client/response/body/error.rs +++ b/src/client/response/body/error.rs @@ -2,5 +2,6 @@ pub enum Error { Buffer, Decode, Format, - Status, + InputStream, + Overflow, } diff --git a/src/client/response/error.rs b/src/client/response/error.rs deleted file mode 100644 index 089f4ae..0000000 --- a/src/client/response/error.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub enum Error { - Header, - Body, -} diff --git a/src/client/response/header.rs b/src/client/response/header.rs index 5fc953c..c673975 100644 --- a/src/client/response/header.rs +++ b/src/client/response/header.rs @@ -8,7 +8,11 @@ pub use meta::Meta; pub use mime::Mime; pub use status::Status; -use glib::Bytes; +use gio::{ + prelude::{IOStreamExt, InputStreamExt}, + Cancellable, SocketConnection, +}; +use glib::{Bytes, Priority}; pub struct Header { status: Status, @@ -21,39 +25,58 @@ pub struct Header { impl Header { // Constructors - pub fn from_response(bytes: &Bytes) -> Result { - // Get header slice of bytes - let end = Self::end(bytes)?; - let bytes = Bytes::from(match bytes.get(..end) { - Some(buffer) => buffer, - None => return Err(Error::Buffer), - }); - - // Status is required, parse to continue - let status = match Status::from_header(&bytes) { - Ok(status) => Ok(status), - Err(reason) => Err(match reason { - status::Error::Decode => Error::StatusDecode, - status::Error::Undefined => Error::StatusUndefined, - }), - }?; - - // Done - Ok(Self { - status, - meta: match Meta::from_header(&bytes) { - Ok(meta) => Some(meta), - Err(_) => None, + pub fn from_socket_connection_async( + socket_connection: SocketConnection, + priority: Option, + cancellable: Option, + callback: impl FnOnce(Result)>) + 'static, + ) { + // Take header buffer from input stream + Self::read_from_socket_connection_async( + Vec::with_capacity(1024), + socket_connection, + match cancellable { + Some(value) => Some(value), + None => None::, }, - mime: match Mime::from_header(&bytes) { - Ok(mime) => Some(mime), - Err(_) => None, + match priority { + Some(value) => value, + None => Priority::DEFAULT, }, - }) + |result| { + callback(match result { + Ok(buffer) => { + // Status is required, parse to continue + match Status::from_header(&buffer) { + Ok(status) => Ok(Self { + status, + meta: match Meta::from_header(&buffer) { + Ok(meta) => Some(meta), + Err(_) => None, + }, + mime: match Mime::from_header(&buffer) { + Ok(mime) => Some(mime), + Err(_) => None, + }, + }), + Err(reason) => Err(( + match reason { + status::Error::Decode => Error::StatusDecode, + status::Error::Undefined => Error::StatusUndefined, + }, + None, + )), + } + } + Err(error) => Err(error), + }) + }, + ); } // Getters + pub fn status(&self) -> &Status { &self.status } @@ -68,13 +91,58 @@ impl Header { // Tools - /// Get last header byte (until \r) - fn end(bytes: &Bytes) -> Result { - for (offset, &byte) in bytes.iter().enumerate() { - if byte == b'\r' { - return Ok(offset); - } - } - Err(Error::Format) + pub fn read_from_socket_connection_async( + mut buffer: Vec, + connection: SocketConnection, + cancellable: Option, + priority: Priority, + callback: impl FnOnce(Result, (Error, Option<&str>)>) + 'static, + ) { + connection.input_stream().read_bytes_async( + 1, // do not change! + priority, + cancellable.clone().as_ref(), + move |result| match result { + Ok(bytes) => { + // Expect valid header length + if bytes.len() == 0 || buffer.len() + 1 > 1024 { + return callback(Err((Error::Protocol, None))); + } + + // Read next byte without buffer record + if bytes.contains(&b'\r') { + return Self::read_from_socket_connection_async( + buffer, + connection, + cancellable, + priority, + callback, + ); + } + + // Complete without buffer record + if bytes.contains(&b'\n') { + return callback(Ok(buffer + .iter() + .flat_map(|byte| byte.iter()) + .cloned() + .collect())); // convert to UTF-8 + } + + // Record + buffer.push(bytes); + + // Continue + Self::read_from_socket_connection_async( + buffer, + connection, + cancellable, + priority, + callback, + ); + } + Err(reason) => callback(Err((Error::InputStream, Some(reason.message())))), + }, + ); } } diff --git a/src/client/response/header/error.rs b/src/client/response/header/error.rs index 4eeff64..e8176bd 100644 --- a/src/client/response/header/error.rs +++ b/src/client/response/header/error.rs @@ -1,6 +1,8 @@ +#[derive(Debug)] pub enum Error { Buffer, - Format, + InputStream, + Protocol, StatusDecode, StatusUndefined, } diff --git a/src/client/response/header/meta.rs b/src/client/response/header/meta.rs index b2e9380..17bf70e 100644 --- a/src/client/response/header/meta.rs +++ b/src/client/response/header/meta.rs @@ -1,7 +1,7 @@ pub mod error; pub use error::Error; -use glib::{Bytes, GString}; +use glib::GString; /// Entire meta buffer, but [status code](https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes). /// @@ -11,13 +11,13 @@ pub struct Meta { } impl Meta { - pub fn from_header(bytes: &Bytes) -> Result { - let buffer = match bytes.get(3..) { - Some(bytes) => bytes.to_vec(), + pub fn from_header(buffer: &[u8]) -> Result { + match buffer.get(3..) { + Some(value) => Ok(Self { + buffer: value.to_vec(), + }), None => return Err(Error::Undefined), - }; - - Ok(Self { buffer }) + } } pub fn to_gstring(&self) -> Result { diff --git a/src/client/response/header/meta/error.rs b/src/client/response/header/meta/error.rs index e0c05eb..d9b19fd 100644 --- a/src/client/response/header/meta/error.rs +++ b/src/client/response/header/meta/error.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub enum Error { Decode, Undefined, diff --git a/src/client/response/header/mime.rs b/src/client/response/header/mime.rs index 8f9e2d9..c3cac44 100644 --- a/src/client/response/header/mime.rs +++ b/src/client/response/header/mime.rs @@ -1,10 +1,11 @@ pub mod error; pub use error::Error; -use glib::{Bytes, GString, Uri}; +use glib::{GString, Uri}; use std::path::Path; /// https://geminiprotocol.net/docs/gemtext-specification.gmi#media-type-parameters +#[derive(Debug)] pub enum Mime { TextGemini, TextPlain, @@ -15,9 +16,9 @@ pub enum Mime { } // @TODO impl Mime { - pub fn from_header(bytes: &Bytes) -> Result { - match bytes.get(..) { - Some(bytes) => match GString::from_utf8(bytes.to_vec()) { + pub fn from_header(buffer: &[u8]) -> Result { + match buffer.get(..) { + Some(value) => match GString::from_utf8(value.to_vec()) { Ok(string) => Self::from_string(string.as_str()), Err(_) => Err(Error::Decode), }, diff --git a/src/client/response/header/mime/error.rs b/src/client/response/header/mime/error.rs index e0c05eb..d9b19fd 100644 --- a/src/client/response/header/mime/error.rs +++ b/src/client/response/header/mime/error.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub enum Error { Decode, Undefined, diff --git a/src/client/response/header/status.rs b/src/client/response/header/status.rs index 908ddf3..f251d3b 100644 --- a/src/client/response/header/status.rs +++ b/src/client/response/header/status.rs @@ -1,9 +1,10 @@ pub mod error; pub use error::Error; -use glib::{Bytes, GString}; +use glib::GString; /// https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes +#[derive(Debug)] pub enum Status { Input, SensitiveInput, @@ -12,9 +13,9 @@ pub enum Status { } // @TODO impl Status { - pub fn from_header(bytes: &Bytes) -> Result { - match bytes.get(0..2) { - Some(bytes) => match GString::from_utf8(bytes.to_vec()) { + pub fn from_header(buffer: &[u8]) -> Result { + match buffer.get(0..2) { + Some(value) => match GString::from_utf8(value.to_vec()) { Ok(string) => Self::from_string(string.as_str()), Err(_) => Err(Error::Decode), }, diff --git a/src/client/response/header/status/error.rs b/src/client/response/header/status/error.rs index e0c05eb..d9b19fd 100644 --- a/src/client/response/header/status/error.rs +++ b/src/client/response/header/status/error.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub enum Error { Decode, Undefined,