1use axum::http::StatusCode;
4use axum::response::{IntoResponse, Response};
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 IntoResponse for HaystackError {
65 fn into_response(self) -> Response {
66 let grid = error_grid(&self.message);
67 match content::encode_response_grid(&grid, "text/zinc") {
68 Ok((body, content_type)) => (
69 self.status,
70 [(axum::http::header::CONTENT_TYPE, content_type)],
71 body,
72 )
73 .into_response(),
74 Err(_) => (
75 self.status,
76 [(axum::http::header::CONTENT_TYPE, "text/plain")],
77 format!("Error: {}", self.message),
78 )
79 .into_response(),
80 }
81 }
82}
83
84pub fn grid_from_cols_rows(col_names: &[&str], rows: Vec<HDict>) -> HGrid {
88 let cols: Vec<HCol> = col_names.iter().map(|n| HCol::new(*n)).collect();
89 HGrid::from_parts(HDict::new(), cols, rows)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn error_grid_has_err_marker() {
98 let grid = error_grid("something went wrong");
99 assert!(grid.is_err());
100 assert_eq!(
101 grid.meta.get("dis"),
102 Some(&Kind::Str("something went wrong".to_string()))
103 );
104 assert!(grid.is_empty());
105 }
106
107 #[test]
108 fn haystack_error_display() {
109 let err = HaystackError::bad_request("invalid filter");
110 assert_eq!(err.to_string(), "invalid filter");
111 assert_eq!(err.status, StatusCode::BAD_REQUEST);
112 }
113
114 #[test]
115 fn grid_from_cols_rows_builds_correctly() {
116 let mut row = HDict::new();
117 row.set("name", Kind::Str("about".into()));
118 row.set("summary", Kind::Str("Summary of about op".into()));
119
120 let grid = grid_from_cols_rows(&["name", "summary"], vec![row]);
121 assert_eq!(grid.len(), 1);
122 assert_eq!(grid.cols.len(), 2);
123 }
124}