ggemini/src/client/response/data/text.rs

118 lines
3.3 KiB
Rust

//! 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<Self, (Error, Option<&str>)> {
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<IOStream>,
priority: Option<Priority>,
cancellable: Option<Cancellable>,
on_complete: impl FnOnce(Result<Self, (Error, Option<&str>)>) + 'static,
) {
read_all_from_stream_async(
Vec::with_capacity(BUFFER_CAPACITY),
stream,
match cancellable {
Some(value) => Some(value),
None => None::<Cancellable>,
},
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<u8>,
stream: impl IsA<IOStream>,
cancelable: Option<Cancellable>,
priority: Priority,
callback: impl FnOnce(Result<Vec<u8>, (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())))),
},
);
}