馃 The Definitive Gemini Protocol Toolkit
gemini
gemini-protocol
gemtext
parser
zero-dependency
toolkit
ast
converter
html
markdown
cli
networking
1use {crate::request::Status, rustls::SupportedCipherSuite, std::borrow::Cow};
2
3#[derive(Debug, Clone, PartialEq)]
4pub struct Response {
5 status: Status,
6 meta: String,
7 content: Option<Vec<u8>>,
8 size: usize,
9 suite: Option<SupportedCipherSuite>,
10}
11
12impl Response {
13 pub(crate) fn new(data: &[u8], suite: Option<SupportedCipherSuite>) -> Self {
14 let delimiter = b"\r\n";
15 let header_end = data
16 .windows(delimiter.len())
17 .position(|window| window == delimiter)
18 .map_or(data.len(), |pos| pos + delimiter.len());
19 let header_bytes = &data[..header_end];
20 let header_cow = String::from_utf8_lossy(header_bytes);
21 let header_trimmed = header_cow.trim_end();
22 let content_bytes = if header_end < data.len() {
23 Some(data[header_end..].to_vec())
24 } else {
25 None
26 };
27 let (status_string, meta_string) = if header_trimmed.len() >= 2 {
28 header_trimmed.split_at(2)
29 } else {
30 (header_trimmed, "")
31 };
32 let status_code = status_string.parse::<i32>().unwrap_or(0);
33
34 Self {
35 status: Status::from(status_code),
36 meta: meta_string.trim_start().to_string(),
37 content: content_bytes,
38 size: data.len(),
39 suite,
40 }
41 }
42
43 #[must_use]
44 pub const fn status(&self) -> &Status { &self.status }
45
46 #[allow(clippy::missing_const_for_fn)]
47 #[must_use]
48 pub fn meta(&self) -> Cow<'_, str> { Cow::Borrowed(&self.meta) }
49
50 /// This associated function assumes that the content is valid UTF-8.
51 ///
52 /// If you want to handle data bytes directly, use
53 /// [`Response::content_bytes`].
54 #[must_use]
55 pub fn content(&self) -> Option<String> {
56 self
57 .content
58 .as_ref()
59 .map(|content| String::from_utf8_lossy(content).to_string())
60 }
61
62 #[must_use]
63 pub fn content_bytes(&self) -> Option<&[u8]> { self.content.as_deref() }
64
65 #[must_use]
66 pub const fn size(&self) -> &usize { &self.size }
67
68 #[must_use]
69 pub const fn suite(&self) -> &Option<SupportedCipherSuite> { &self.suite }
70}