tibba_error/
lib.rs

1// Copyright 2025 Tree xie.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Import required dependencies for HTTP handling, serialization, and logging
16use axum::Json;
17use axum::http::{HeaderValue, StatusCode, header};
18use axum::response::{IntoResponse, Response};
19use serde::{Deserialize, Serialize};
20use snafu::Snafu;
21
22/// Main Error enum that wraps HttpError
23/// Uses Snafu for error handling boilerplate generation
24#[derive(Debug, Snafu, Default, Serialize, Deserialize, Clone)]
25#[snafu(display("{message}"))]
26pub struct Error {
27    // HTTP status code
28    #[serde(skip)]
29    pub status: u16,
30    // error category
31    pub category: String,
32    // error message
33    pub message: String,
34    // sub-category
35    pub sub_category: Option<String>,
36    // error code
37    pub code: Option<String>,
38    // whether it is an exception
39    pub exception: Option<bool>,
40    // other extra information
41    pub extra: Option<Box<Vec<String>>>,
42}
43
44impl Error {
45    #[must_use]
46    pub fn new(message: impl ToString) -> Self {
47        Self {
48            message: message.to_string(),
49            ..Default::default()
50        }
51    }
52    /// Sets the error category
53    #[must_use]
54    pub fn with_category(mut self, category: impl ToString) -> Self {
55        self.category = category.to_string();
56        self
57    }
58    /// Sets the sub-category
59    #[must_use]
60    pub fn with_sub_category(mut self, sub_category: impl ToString) -> Self {
61        self.sub_category = Some(sub_category.to_string());
62        self
63    }
64    /// Sets the error code
65    #[must_use]
66    pub fn with_code(mut self, code: impl ToString) -> Self {
67        self.code = Some(code.to_string());
68        self
69    }
70    /// Sets the HTTP status code
71    #[must_use]
72    pub fn with_status(mut self, status: u16) -> Self {
73        self.status = status;
74        self
75    }
76    /// Sets whether it is an exception
77    #[must_use]
78    pub fn with_exception(mut self, exception: bool) -> Self {
79        self.exception = Some(exception);
80        self
81    }
82    /// Adds extra information
83    #[must_use]
84    pub fn add_extra(mut self, value: impl ToString) -> Self {
85        self.extra
86            .get_or_insert_with(Box::default)
87            .push(value.to_string());
88        self
89    }
90}
91
92/// Implements conversion of Error into HTTP Response
93/// Sets appropriate status code and headers
94impl IntoResponse for Error {
95    fn into_response(self) -> Response {
96        let status = StatusCode::from_u16(self.status).unwrap_or(StatusCode::BAD_REQUEST);
97        // for error, set no-cache
98        let mut res = Json(&self).into_response();
99        res.extensions_mut().insert(self);
100        res.headers_mut()
101            .insert(header::CACHE_CONTROL, HeaderValue::from_static("no-cache"));
102        (status, res).into_response()
103    }
104}