anvil/eth/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 internal_error_with_details(error: impl Display) -> Self {
50 Self::new(BeaconErrorCode::InternalError, format!("Internal server error: {error}"))
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 InternalError = 500,
90}
91
92impl BeaconErrorCode {
93 pub const fn status_code(&self) -> u16 {
95 *self as u16
96 }
97
98 pub const fn as_str(&self) -> &'static str {
100 match self {
101 Self::BadRequest => "Bad Request",
102 Self::NotFound => "Not Found",
103 Self::InternalError => "Internal Server Error",
104 }
105 }
106}
107
108impl fmt::Display for BeaconErrorCode {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 write!(f, "{}", self.as_str())
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_beacon_error_codes() {
120 assert_eq!(BeaconErrorCode::BadRequest.status_code(), 400);
121 assert_eq!(BeaconErrorCode::NotFound.status_code(), 404);
122 assert_eq!(BeaconErrorCode::InternalError.status_code(), 500);
123 }
124
125 #[test]
126 fn test_beacon_error_display() {
127 let err = BeaconError::invalid_block_id("current");
128 assert_eq!(err.to_string(), "Bad Request: Invalid block ID: current");
129 }
130}