//! Tools for Text-based response pub mod error; pub use error::Error; // Local dependencies use gio::{ prelude::{IOStreamExt, InputStreamExt}, Cancellable, IOStream, }; use glib::{object::IsA, GString, Priority}; // Default limits pub const BUFFER_CAPACITY: usize = 0x400; // 1024 pub const BUFFER_MAX_SIZE: usize = 0xfffff; // 1M /// Container for text-based response data pub struct Text { data: GString, } impl Text { // Constructors /// Create new `Self` pub fn new() -> Self { Self { data: GString::new(), } } /// Create new `Self` from string pub fn from_string(data: &str) -> Self { Self { data: data.into() } } /// Create new `Self` from UTF-8 buffer pub fn from_utf8(buffer: &[u8]) -> Result)> { match GString::from_utf8(buffer.into()) { Ok(data) => Ok(Self::from_string(&data)), Err(_) => Err((Error::Decode, None)), } } /// Asynchronously create new `Self` from [IOStream](https://docs.gtk.org/gio/class.IOStream.html) pub fn from_stream_async( stream: impl IsA, priority: Option, cancellable: Option, on_complete: impl FnOnce(Result)>) + 'static, ) { read_all_from_stream_async( Vec::with_capacity(BUFFER_CAPACITY), stream, match cancellable { Some(value) => Some(value), None => None::, }, match priority { Some(value) => value, None => Priority::DEFAULT, }, |result| match result { Ok(buffer) => on_complete(Self::from_utf8(&buffer)), Err(reason) => on_complete(Err(reason)), }, ); } // Getters /// Get reference to `Self` data pub fn data(&self) -> &GString { &self.data } } // Tools /// Asynchronously read all bytes from [IOStream](https://docs.gtk.org/gio/class.IOStream.html) /// /// Return UTF-8 buffer collected /// * require `IOStream` reference to keep `Connection` active in async thread pub fn read_all_from_stream_async( mut buffer: Vec, stream: impl IsA, cancelable: Option, priority: Priority, callback: impl FnOnce(Result, (Error, Option<&str>)>) + 'static, ) { stream.input_stream().read_bytes_async( BUFFER_CAPACITY, priority, cancelable.clone().as_ref(), move |result| match result { Ok(bytes) => { // No bytes were read, end of stream if bytes.len() == 0 { return callback(Ok(buffer)); } // Validate overflow if buffer.len() + bytes.len() > BUFFER_MAX_SIZE { return callback(Err((Error::BufferOverflow, None))); } // Save chunks to buffer for &byte in bytes.iter() { buffer.push(byte); } // Continue bytes reading read_all_from_stream_async(buffer, stream, cancelable, priority, callback); } Err(reason) => callback(Err((Error::InputStream, Some(reason.message())))), }, ); }