#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct RawBody {
headers: Vec<String>,
body: Option<String>,
}
impl RawBody {
#[must_use]
pub fn new(headers: Vec<String>, body: String) -> Self {
Self {
headers,
body: Some(body),
}
}
#[must_use]
pub fn new_empty(headers: Vec<String>) -> Self {
Self {
headers,
body: None,
}
}
pub fn headers_lines(&self) -> impl Iterator<Item = &str> {
self.headers.iter().map(String::as_str)
}
#[must_use]
pub const fn body(&self) -> &Option<String> {
&self.body
}
#[must_use]
pub fn headers(&self) -> Vec<(String, String)> {
let mut out = vec![];
for (idx, header) in self.headers.iter().enumerate() {
if header.starts_with(' ') || header.starts_with('\t') {
continue;
}
let mut split = header.splitn(2, ':');
match (split.next(), split.next()) {
(Some(key), Some(value)) => {
let mut s = value.to_string();
for i in self.headers[idx + 1..]
.iter()
.take_while(|s| s.starts_with(' ') || s.starts_with('\t'))
{
s.push_str(i);
}
out.push((key.to_string(), s));
}
_ => continue,
}
}
out
}
#[must_use]
pub const fn raw_headers(&self) -> &Vec<String> {
&self.headers
}
#[must_use]
pub fn get_header(&self, name: &str, with_key: bool) -> Option<String> {
for (idx, header) in self.headers.iter().enumerate() {
if header.starts_with(' ') || header.starts_with('\t') {
continue;
}
let mut split = header.splitn(2, ':');
match (split.next(), split.next()) {
(Some(key), Some(value)) if key.eq_ignore_ascii_case(name) => {
let mut value = value.to_string();
for i in self.headers[idx + 1..]
.iter()
.take_while(|s| s.starts_with(' ') || s.starts_with('\t'))
{
value.push_str(i);
}
return Some(if with_key {
format!("{key}:{value}")
} else {
value.trim_start().to_string()
});
}
(Some(_), Some(_)) => continue,
_ => break,
}
}
None
}
#[must_use]
pub fn count_header(&self, name: &str) -> usize {
self.headers
.iter()
.filter(|h| {
h.to_lowercase()
.starts_with(&format!("{name}:").to_lowercase())
})
.count()
}
pub fn set_header(&mut self, name: &str, value: &str) {
for header in &mut self.headers {
let mut split = header.splitn(2, ": ");
match (split.next(), split.next()) {
(Some(key), Some(_)) if key.eq_ignore_ascii_case(name) => {
*header = format!("{key}: {value}");
return;
}
_ => {}
}
}
self.add_header(name, value);
}
pub fn rename_header(&mut self, old: &str, new: &str) {
for header in &mut self.headers {
let mut split = header.splitn(2, ": ");
match (split.next(), split.next()) {
(Some(key), Some(value)) if key.eq_ignore_ascii_case(old) => {
*header = format!("{new}: {value}");
return;
}
_ => {}
}
}
}
pub fn add_header(&mut self, name: &str, value: &str) {
self.headers.push(format!("{name}: {value}"));
}
pub fn prepend_header(&mut self, headers: impl IntoIterator<Item = String>) {
self.headers.splice(..0, headers);
}
pub fn remove_header(&mut self, name: &str) -> bool {
if let Some(index) = self.headers.iter().position(|header| {
header
.to_lowercase()
.starts_with(&format!("{}:", name.to_lowercase()))
}) {
self.headers.remove(index);
true
} else {
false
}
}
}
impl std::fmt::Display for RawBody {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for i in &self.headers {
f.write_str(i)?;
}
f.write_str("\r\n")?;
if let Some(body) = &self.body {
f.write_str(body)?;
}
Ok(())
}
}