use crate::error::ResponseErr;
use openssl::{hash::MessageDigest, x509::X509};
use std::{fs::File, io::Write, path::PathBuf};
#[derive(Debug)]
pub struct Response {
pub status: u8,
pub meta: String,
pub content: Vec<u8>,
pub certificate: X509,
}
type Result<T> = std::result::Result<T, ResponseErr>;
impl Response {
pub fn is_gemtext(&self) -> bool {
if let Some(pos) = self.meta.find("text/gemini") {
if pos == 0 {
return true;
}
}
false
}
pub fn gemtext(&self) -> Result<String> {
self.require_status(20)?;
if self.is_gemtext() {
return self.text();
}
Err(ResponseErr::UnexpectedFiletype(
"text/gemini".to_string(),
self.meta.clone(),
))
}
pub fn text(&self) -> Result<String> {
self.require_status(20)?;
Ok(std::str::from_utf8(&self.content)
.map_err(ResponseErr::Utf8Content)?
.to_string())
}
pub fn save(&self, file: &mut File) -> Result<()> {
self.require_status(20)?;
file.write_all(&self.content)
.map_err(ResponseErr::FileWrite)?;
Ok(())
}
pub fn save_to_path(&self, path: impl Into<PathBuf>) -> Result<()> {
self.require_status(20)?;
let path = path.into();
let mut file = File::create(path).map_err(ResponseErr::FileCreate)?;
file.write_all(&self.content)
.map_err(ResponseErr::FileWrite)?;
Ok(())
}
pub fn certificate_pem(&self) -> Result<String> {
Ok(std::str::from_utf8(
&self
.certificate
.to_pem()
.map_err(ResponseErr::SerializingToPem)?,
)
.map_err(ResponseErr::PemInvalidUtf8)?
.to_string())
}
pub fn certificate_info(&self) -> Result<String> {
Ok(std::str::from_utf8(
&self
.certificate
.to_text()
.map_err(ResponseErr::FailedToInspectCert)?,
)
.map_err(ResponseErr::CertInfoIsntValidUtf8)?
.to_string())
}
pub fn certificate_fingerprint(&self) -> Result<String> {
use std::fmt::Write;
Ok(self
.certificate
.digest(MessageDigest::sha256())
.map_err(ResponseErr::FailedToFingerprint)?
.iter()
.fold(String::new(), |mut out, x| {
let _ = write!(out, "{:02x}", x);
out
}))
}
fn require_status(&self, s: u8) -> Result<()> {
if self.status != s {
Err(ResponseErr::UnexpectedStatus {
expected: s.into(),
received: self.status.into(),
meta: self.meta.clone(),
})
} else {
Ok(())
}
}
}