use std::str;
use crate::{
gemtext::Builder,
header::Header,
status::{Category, Status},
};
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Response {
pub header: Header,
body: Option<Vec<u8>>,
}
impl Response {
pub fn status(&self) -> Status {
self.header.status
}
pub fn has_body(&self) -> bool {
self.body.is_some()
}
pub fn new(header: Header, body: Option<Vec<u8>>) -> Option<Self> {
match (header.status.category(), &body) {
(Category::Success, Some(_)) | (_, None) => Some(Response { header, body }),
_ => None,
}
}
pub fn with_body(mime_type: String, body: Vec<u8>) -> Option<Self> {
let header = Header::new(Status::SUCCESS, mime_type)?;
let body = Some(body);
Some(Response { header, body })
}
pub fn with_gemtext(builder: Builder) -> Self {
let header = Header::gemtext();
let body = Some(builder.build().bytes().collect());
Self::new_unchecked(header, body)
}
pub fn without_body(header: Header) -> Option<Self> {
Self::new(header, None)
}
pub fn new_unchecked(header: Header, body: Option<Vec<u8>>) -> Self {
Response { header, body }
}
pub fn body_text(&self) -> Option<&str> {
let bytes = self.body.as_ref()?;
str::from_utf8(bytes).ok()
}
pub fn body_bytes(&self) -> Option<&[u8]> {
self.body.as_deref()
}
}
#[cfg(feature = "parsers")]
pub mod parse {
use nom::{
combinator::{map_opt, rest},
error::context,
sequence::pair,
IResult,
};
use super::*;
use crate::header::parse::header;
pub fn response(input: &[u8]) -> IResult<&[u8], Response> {
context(
"response",
map_opt(pair(header, rest), |t| {
if t.1.is_empty() {
Response::new(t.0, None)
} else {
let v = Vec::from(t.1);
Response::new(t.0, Some(v))
}
}),
)(input)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_response() {
let bytes = b"20 text/gemini\r\n=> gemini://foo.bar.baz/ wow";
let res = response(bytes).unwrap().1;
assert_eq!(res.body_text().unwrap(), "=> gemini://foo.bar.baz/ wow")
}
#[test]
fn test_response_no_body() {
let bytes = b"60 owwwwwwww!\r\ni shouldn't be here";
assert!(response(bytes).is_err());
let bytes = b"61 this is fine\r\n";
assert!(response(bytes).is_ok());
}
}
}