anvil/server/beacon/
error.rs1use axum::{
4 Json,
5 http::StatusCode,
6 response::{IntoResponse, Response},
7};
8use serde::{Deserialize, Serialize};
9use std::{
10 borrow::Cow,
11 fmt::{self, Display},
12};
13
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub struct BeaconError {
17 #[serde(skip)]
19 pub status_code: u16,
20 pub code: BeaconErrorCode,
22 pub message: Cow<'static, str>,
24}
25
26impl BeaconError {
27 pub fn new(code: BeaconErrorCode, message: impl Into<Cow<'static, str>>) -> Self {
29 let status_code = code.status_code();
30 Self { status_code, code, message: message.into() }
31 }
32
33 pub fn invalid_block_id(block_id: impl Display) -> Self {
35 Self::new(BeaconErrorCode::BadRequest, format!("Invalid block ID: {block_id}"))
36 }
37
38 pub fn block_not_found() -> Self {
40 Self::new(BeaconErrorCode::NotFound, "Block not found")
41 }
42
43 pub fn internal_error() -> Self {
45 Self::new(BeaconErrorCode::InternalError, "Internal server error")
46 }
47
48 pub fn deprecated_endpoint_with_hint(hint: impl Display) -> Self {
50 Self::new(BeaconErrorCode::Gone, format!("This endpoint is deprecated. {hint}"))
51 }
52
53 pub fn into_response(self) -> Response {
55 let status =
56 StatusCode::from_u16(self.status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
57
58 (
59 status,
60 Json(serde_json::json!({
61 "code": self.code as u16,
62 "message": self.message,
63 })),
64 )
65 .into_response()
66 }
67}
68
69impl fmt::Display for BeaconError {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "{}: {}", self.code.as_str(), self.message)
72 }
73}
74
75impl std::error::Error for BeaconError {}
76
77impl IntoResponse for BeaconError {
78 fn into_response(self) -> Response {
79 Self::into_response(self)
80 }
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
85#[repr(u16)]
86pub enum BeaconErrorCode {
87 BadRequest = 400,
88 NotFound = 404,
89 Gone = 410,
90 InternalError = 500,
91}
92
93impl BeaconErrorCode {
94 pub const fn status_code(&self) -> u16 {
96 *self as u16
97 }
98
99 pub const fn as_str(&self) -> &'static str {
101 match self {
102 Self::BadRequest => "Bad Request",
103 Self::NotFound => "Not Found",
104 Self::Gone => "Gone",
105 Self::InternalError => "Internal Server Error",
106 }
107 }
108}
109
110impl fmt::Display for BeaconErrorCode {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(f, "{}", self.as_str())
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_beacon_error_codes() {
122 assert_eq!(BeaconErrorCode::BadRequest.status_code(), 400);
123 assert_eq!(BeaconErrorCode::NotFound.status_code(), 404);
124 assert_eq!(BeaconErrorCode::InternalError.status_code(), 500);
125 }
126
127 #[test]
128 fn test_beacon_error_display() {
129 let err = BeaconError::invalid_block_id("current");
130 assert_eq!(err.to_string(), "Bad Request: Invalid block ID: current");
131 }
132}