1use crate::error::ResponseErr;
2use openssl::{hash::MessageDigest, x509::X509};
3use std::{fs::File, io::Write, path::PathBuf};
4
5#[derive(Debug)]
7pub struct Response {
8 pub status: u8,
9 pub meta: String,
10 pub content: Vec<u8>,
11 pub certificate: X509,
13}
14
15type Result<T> = std::result::Result<T, ResponseErr>;
16
17impl Response {
18 pub fn is_gemtext(&self) -> bool {
20 if let Some(pos) = self.meta.find("text/gemini") {
21 if pos == 0 {
22 return true;
23 }
24 }
25 false
26 }
27
28 pub fn gemtext(&self) -> Result<String> {
30 self.require_status(20)?;
31
32 if self.is_gemtext() {
33 return self.text();
34 }
35
36 Err(ResponseErr::UnexpectedFiletype(
37 "text/gemini".to_string(),
38 self.meta.clone(),
39 ))
40 }
41
42 pub fn text(&self) -> Result<String> {
44 self.require_status(20)?;
45 Ok(std::str::from_utf8(&self.content)
46 .map_err(ResponseErr::Utf8Content)?
47 .to_string())
48 }
49
50 pub fn save(&self, file: &mut File) -> Result<()> {
52 self.require_status(20)?;
53 file.write_all(&self.content)
54 .map_err(ResponseErr::FileWrite)?;
55 Ok(())
56 }
57
58 pub fn save_to_path(&self, path: impl Into<PathBuf>) -> Result<()> {
60 self.require_status(20)?;
61
62 let path = path.into();
63 let mut file = File::create(path).map_err(ResponseErr::FileCreate)?;
64 file.write_all(&self.content)
65 .map_err(ResponseErr::FileWrite)?;
66 Ok(())
67 }
68
69 pub fn certificate_pem(&self) -> Result<String> {
71 Ok(std::str::from_utf8(
72 &self
73 .certificate
74 .to_pem()
75 .map_err(ResponseErr::SerializingToPem)?,
76 )
77 .map_err(ResponseErr::PemInvalidUtf8)?
78 .to_string())
79 }
80
81 pub fn certificate_info(&self) -> Result<String> {
86 Ok(std::str::from_utf8(
87 &self
88 .certificate
89 .to_text()
90 .map_err(ResponseErr::FailedToInspectCert)?,
91 )
92 .map_err(ResponseErr::CertInfoIsntValidUtf8)?
93 .to_string())
94 }
95
96 pub fn certificate_fingerprint(&self) -> Result<String> {
98 use std::fmt::Write;
99 Ok(self
100 .certificate
101 .digest(MessageDigest::sha256())
102 .map_err(ResponseErr::FailedToFingerprint)?
103 .iter()
104 .fold(String::new(), |mut out, x| {
105 let _ = write!(out, "{:02x}", x);
106 out
107 }))
108 }
109
110 fn require_status(&self, s: u8) -> Result<()> {
112 if self.status != s {
113 Err(ResponseErr::UnexpectedStatus {
114 expected: s.into(),
115 received: self.status.into(),
116 meta: self.meta.clone(),
117 })
118 } else {
119 Ok(())
120 }
121 }
122}