Skip to content

Commit 20270d6

Browse files
author
yggverse
committed
move body component to data::text namespace
1 parent d5cea24 commit 20270d6

File tree

6 files changed

+127
-210
lines changed

6 files changed

+127
-210
lines changed

src/client/response.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
//! Read and parse Gemini response as GObject
1+
//! Read and parse Gemini response as Object
22
3-
pub mod body;
43
pub mod data;
54
pub mod meta;
65

7-
pub use body::Body;
86
pub use meta::Meta;

src/client/response/body.rs

Lines changed: 0 additions & 200 deletions
This file was deleted.

src/client/response/body/error.rs

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/client/response/data.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Gemini response could have different MIME type for data.
2-
//! Use one of these components to parse response according to content type expected.
2+
//! Use one of components below to parse response according to content type expected.
33
//!
44
//! * MIME type could be detected using `client::response::Meta` parser
55
66
pub mod text;
7+
pub use text::Text;

src/client/response/data/text.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//! Tools for Text-based response
2+
3+
pub mod error;
4+
pub use error::Error;
5+
6+
// Local dependencies
7+
use gio::{
8+
prelude::{IOStreamExt, InputStreamExt},
9+
Cancellable, SocketConnection,
10+
};
11+
use glib::{GString, Priority};
12+
13+
// Default limits
14+
pub const BUFFER_CAPACITY: usize = 0x400; // 1024
15+
pub const BUFFER_MAX_SIZE: usize = 0xfffff; // 1M
16+
17+
/// Container for text-based response data
18+
pub struct Text {
19+
data: GString,
20+
}
21+
22+
impl Text {
23+
// Constructors
24+
25+
/// Create new `Self` with options
26+
pub fn new(data: GString) -> Self {
27+
Self { data }
28+
}
29+
30+
/// Create new `Self` from UTF-8 buffer
31+
pub fn from_utf8(buffer: &[u8]) -> Result<Self, (Error, Option<&str>)> {
32+
match GString::from_utf8(buffer.into()) {
33+
Ok(data) => Ok(Self::new(data)),
34+
Err(_) => Err((Error::Decode, None)),
35+
}
36+
}
37+
38+
/// Asynchronously create new `Self` from [InputStream](https://docs.gtk.org/gio/class.InputStream.html)
39+
/// for given [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html)
40+
pub fn from_socket_connection_async(
41+
socket_connection: SocketConnection,
42+
priority: Option<Priority>,
43+
cancellable: Option<Cancellable>,
44+
on_complete: impl FnOnce(Result<Self, (Error, Option<&str>)>) + 'static,
45+
) {
46+
read_all_from_socket_connection_async(
47+
Vec::with_capacity(BUFFER_CAPACITY),
48+
socket_connection,
49+
match cancellable {
50+
Some(value) => Some(value),
51+
None => None::<Cancellable>,
52+
},
53+
match priority {
54+
Some(value) => value,
55+
None => Priority::DEFAULT,
56+
},
57+
|result| match result {
58+
Ok(buffer) => on_complete(Self::from_utf8(&buffer)),
59+
Err(reason) => on_complete(Err(reason)),
60+
},
61+
);
62+
}
63+
64+
// Getters
65+
66+
/// Get reference to `Self` data
67+
pub fn data(&self) -> &GString {
68+
&self.data
69+
}
70+
}
71+
72+
// Tools
73+
74+
/// Asynchronously read all bytes from [InputStream](https://docs.gtk.org/gio/class.InputStream.html)
75+
/// for given [SocketConnection](https://docs.gtk.org/gio/class.SocketConnection.html)
76+
///
77+
/// Return UTF-8 buffer collected.
78+
///
79+
/// * this function implements low-level helper for `Text::from_socket_connection_async`, also provides public API for external integrations
80+
/// * requires `SocketConnection` instead of `InputStream` to keep connection alive (by increasing reference count in async context) @TODO
81+
pub fn read_all_from_socket_connection_async(
82+
mut buffer: Vec<u8>,
83+
socket_connection: SocketConnection,
84+
cancelable: Option<Cancellable>,
85+
priority: Priority,
86+
callback: impl FnOnce(Result<Vec<u8>, (Error, Option<&str>)>) + 'static,
87+
) {
88+
socket_connection.input_stream().read_bytes_async(
89+
BUFFER_CAPACITY,
90+
priority,
91+
cancelable.clone().as_ref(),
92+
move |result| match result {
93+
Ok(bytes) => {
94+
// No bytes were read, end of stream
95+
if bytes.len() == 0 {
96+
return callback(Ok(buffer));
97+
}
98+
99+
// Validate overflow
100+
if buffer.len() + bytes.len() > BUFFER_MAX_SIZE {
101+
return callback(Err((Error::BufferOverflow, None)));
102+
}
103+
104+
// Save chunks to buffer
105+
for &byte in bytes.iter() {
106+
buffer.push(byte);
107+
}
108+
109+
// Continue bytes reading
110+
read_all_from_socket_connection_async(
111+
buffer,
112+
socket_connection,
113+
cancelable,
114+
priority,
115+
callback,
116+
);
117+
}
118+
Err(reason) => callback(Err((Error::InputStream, Some(reason.message())))),
119+
},
120+
);
121+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#[derive(Debug)]
22
pub enum Error {
3-
// nothing yet..
3+
BufferOverflow,
4+
Decode,
5+
InputStream,
46
}

0 commit comments

Comments
 (0)