titanite/src/response/certificate/not_valid.rs
2025-02-24 03:05:13 +02:00

60 lines
1.7 KiB
Rust

use anyhow::{bail, Result};
pub const CODE: &[u8] = b"62";
/// [Certificate invalid](https://geminiprotocol.net/docs/protocol-specification.gmi#status-62-certificate-not-valid)
pub struct NotValid {
pub message: Option<String>,
}
impl NotValid {
/// Build `Self` from UTF-8 header bytes
/// * expected buffer includes leading status code, message, CRLF
pub fn from_bytes(buffer: &[u8]) -> Result<Self> {
use crate::tool::Header;
let h = buffer.header_bytes()?;
if h.get(..2)
.is_none_or(|c| c[0] != CODE[0] || c[1] != CODE[1])
{
bail!("Invalid status code")
}
Ok(Self {
message: String::from_utf8((h[3..]).to_vec()).map(|m| {
if m.is_empty() {
None
} else {
Some(m)
}
})?,
})
}
/// Convert `Self` into UTF-8 bytes presentation
pub fn into_bytes(self) -> Vec<u8> {
match self.message {
Some(message) => {
let mut bytes = Vec::with_capacity(message.len() + 5);
bytes.extend(CODE);
bytes.push(b' ');
bytes.extend(message.into_bytes());
bytes.extend([b'\r', b'\n']);
bytes
}
None => {
let mut bytes = Vec::with_capacity(4);
bytes.extend(CODE);
bytes.extend([b'\r', b'\n']);
bytes
}
}
}
}
#[test]
fn test() {
let request = "62 message\r\n";
let source = NotValid::from_bytes(request.as_bytes()).unwrap();
assert_eq!(source.message, Some("message".to_string()));
assert_eq!(source.into_bytes(), request.as_bytes());
}