pub struct DashboardResponse {
pub status: u16,
pub content_type: String,
pub body: Vec<u8>,
pub headers: Vec<(String, String)>,
}
impl DashboardResponse {
pub fn new(status: u16, content_type: impl Into<String>, body: Vec<u8>) -> Self {
Self {
status,
content_type: content_type.into(),
body,
headers: Vec::new(),
}
}
pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.push((name.into(), value.into()));
self
}
pub fn html(body: impl Into<String>) -> Self {
Self::new(200, "text/html", body.into().into_bytes())
}
pub fn html_status(status: u16, body: impl Into<String>) -> Self {
Self::new(status, "text/html", body.into().into_bytes())
}
pub fn text(status: u16, body: impl Into<String>) -> Self {
Self::new(status, "text/plain; charset=utf-8", body.into().into_bytes())
}
pub fn json(value: &serde_json::Value) -> Self {
Self::json_status(200, value)
}
pub fn json_status(status: u16, value: &serde_json::Value) -> Self {
let body = serde_json::to_vec(value).unwrap_or_else(|_| b"{}".to_vec());
Self::new(status, "application/json", body)
}
pub fn csv(body: Vec<u8>, filename: &str) -> Self {
Self::new(200, "text/csv", body).with_header(
"Content-Disposition",
format!("attachment; filename={}", filename),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn html_defaults_to_200_text_html() {
let r = DashboardResponse::html("<p>hi</p>");
assert_eq!(r.status, 200);
assert_eq!(r.content_type, "text/html");
assert_eq!(r.body, b"<p>hi</p>");
assert!(r.headers.is_empty());
}
#[test]
fn html_status_uses_given_status() {
let r = DashboardResponse::html_status(500, "boom");
assert_eq!(r.status, 500);
assert_eq!(r.content_type, "text/html");
}
#[test]
fn text_is_plain() {
let r = DashboardResponse::text(500, "Redis error");
assert_eq!(r.status, 500);
assert_eq!(r.content_type, "text/plain; charset=utf-8");
assert_eq!(r.body, b"Redis error");
}
#[test]
fn json_status_serializes_compactly() {
let r = DashboardResponse::json_status(404, &serde_json::json!({"error": "job not found"}));
assert_eq!(r.status, 404);
assert_eq!(r.content_type, "application/json");
assert_eq!(r.body, br#"{"error":"job not found"}"#);
}
#[test]
fn csv_sets_attachment_disposition() {
let r = DashboardResponse::csv(b"a,b\n1,2\n".to_vec(), "queue_default.csv");
assert_eq!(r.status, 200);
assert_eq!(r.content_type, "text/csv");
let disposition = r
.headers
.iter()
.find(|(k, _)| k == "Content-Disposition")
.map(|(_, v)| v.as_str());
assert_eq!(
disposition,
Some("attachment; filename=queue_default.csv")
);
}
#[test]
fn with_header_appends() {
let r = DashboardResponse::html("x").with_header("X-Test", "1");
assert!(r.headers.iter().any(|(k, v)| k == "X-Test" && v == "1"));
}
}