1use actix_web::http::StatusCode;
4use actix_web::{HttpResponse, ResponseError};
5use std::fmt;
6
7use haystack_core::data::{HCol, HDict, HGrid};
8use haystack_core::kinds::Kind;
9
10use crate::content;
11
12pub fn error_grid(message: &str) -> HGrid {
17 let mut meta = HDict::new();
18 meta.set("err", Kind::Marker);
19 meta.set("dis", Kind::Str(message.to_string()));
20 HGrid::from_parts(meta, vec![], vec![])
21}
22
23#[derive(Debug)]
25pub struct HaystackError {
26 pub message: String,
27 pub status: StatusCode,
28}
29
30impl HaystackError {
31 pub fn new(message: impl Into<String>, status: StatusCode) -> Self {
32 Self {
33 message: message.into(),
34 status,
35 }
36 }
37
38 pub fn bad_request(message: impl Into<String>) -> Self {
39 Self::new(message, StatusCode::BAD_REQUEST)
40 }
41
42 pub fn not_found(message: impl Into<String>) -> Self {
43 Self::new(message, StatusCode::NOT_FOUND)
44 }
45
46 pub fn internal(message: impl Into<String>) -> Self {
47 Self::new(message, StatusCode::INTERNAL_SERVER_ERROR)
48 }
49
50 pub fn forbidden(message: impl Into<String>) -> Self {
51 Self::new(message, StatusCode::FORBIDDEN)
52 }
53}
54
55impl fmt::Display for HaystackError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(f, "{}", self.message)
58 }
59}
60
61impl ResponseError for HaystackError {
62 fn status_code(&self) -> StatusCode {
63 self.status
64 }
65
66 fn error_response(&self) -> HttpResponse {
67 let grid = error_grid(&self.message);
68 match content::encode_response_grid(&grid, "text/zinc") {
70 Ok((body, content_type)) => HttpResponse::build(self.status)
71 .content_type(content_type)
72 .body(body),
73 Err(_) => HttpResponse::build(self.status)
74 .content_type("text/plain")
75 .body(format!("Error: {}", self.message)),
76 }
77 }
78}
79
80pub fn grid_from_cols_rows(col_names: &[&str], rows: Vec<HDict>) -> HGrid {
84 let cols: Vec<HCol> = col_names.iter().map(|n| HCol::new(*n)).collect();
85 HGrid::from_parts(HDict::new(), cols, rows)
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn error_grid_has_err_marker() {
94 let grid = error_grid("something went wrong");
95 assert!(grid.is_err());
96 assert_eq!(
97 grid.meta.get("dis"),
98 Some(&Kind::Str("something went wrong".to_string()))
99 );
100 assert!(grid.is_empty());
101 }
102
103 #[test]
104 fn haystack_error_display() {
105 let err = HaystackError::bad_request("invalid filter");
106 assert_eq!(err.to_string(), "invalid filter");
107 assert_eq!(err.status_code(), StatusCode::BAD_REQUEST);
108 }
109
110 #[test]
111 fn haystack_error_response_is_grid() {
112 let err = HaystackError::internal("test error");
113 let resp = err.error_response();
114 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
115 }
116
117 #[test]
118 fn grid_from_cols_rows_builds_correctly() {
119 let mut row = HDict::new();
120 row.set("name", Kind::Str("about".into()));
121 row.set("summary", Kind::Str("Summary of about op".into()));
122
123 let grid = grid_from_cols_rows(&["name", "summary"], vec![row]);
124 assert_eq!(grid.len(), 1);
125 assert_eq!(grid.cols.len(), 2);
126 }
127}