1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub struct ErrorResponse {
11 pub code: ErrorCode,
12 pub message: String,
13}
14
15impl ErrorResponse {
16 pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
17 Self {
18 code,
19 message: message.into(),
20 }
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum ErrorCode {
28 BadRequest,
30 Unauthorized,
34 PayloadTooLarge,
38 UnsupportedTarget,
41 RenderFailed,
44 Internal,
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn error_response_round_trips_through_json() {
54 let err = ErrorResponse::new(ErrorCode::RenderFailed, "typst: unclosed delimiter");
55
56 let json = serde_json::to_string(&err).unwrap();
57 assert_eq!(
58 json,
59 r#"{"code":"render_failed","message":"typst: unclosed delimiter"}"#
60 );
61 assert_eq!(serde_json::from_str::<ErrorResponse>(&json).unwrap(), err);
62 }
63
64 #[test]
65 fn error_codes_serialize_snake_case() {
66 for (code, expected) in [
67 (ErrorCode::BadRequest, "\"bad_request\""),
68 (ErrorCode::Unauthorized, "\"unauthorized\""),
69 (ErrorCode::PayloadTooLarge, "\"payload_too_large\""),
70 (ErrorCode::UnsupportedTarget, "\"unsupported_target\""),
71 (ErrorCode::RenderFailed, "\"render_failed\""),
72 (ErrorCode::Internal, "\"internal\""),
73 ] {
74 let json = serde_json::to_string(&code).unwrap();
75 assert_eq!(json, expected);
76 assert_eq!(serde_json::from_str::<ErrorCode>(&json).unwrap(), code);
77 }
78 }
79}