roa_core/err.rs
1use std::fmt::{Display, Formatter};
2use std::result::Result as StdResult;
3
4pub use http::StatusCode;
5
6/// Type alias for `StdResult`.
7pub type Result<R = ()> = StdResult<R, Status>;
8
9/// Construct a `Status`.
10///
11/// - `status!(status_code)` will be expanded to `status!(status_code, "")`
12/// - `status!(status_code, message)` will be expanded to `status!(status_code, message, true)`
13/// - `status!(status_code, message, expose)` will be expanded to `Status::new(status_code, message, expose)`
14///
15/// ### Example
16/// ```rust
17/// use roa_core::{App, Context, Next, Result, status};
18/// use roa_core::http::StatusCode;
19///
20/// let app = App::new()
21/// .gate(gate)
22/// .end(status!(StatusCode::IM_A_TEAPOT, "I'm a teapot!"));
23/// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {
24/// next.await?; // throw
25/// unreachable!();
26/// ctx.resp.status = StatusCode::OK;
27/// Ok(())
28/// }
29/// ```
30#[macro_export]
31macro_rules! status {
32 ($status_code:expr) => {
33 $crate::status!($status_code, "")
34 };
35 ($status_code:expr, $message:expr) => {
36 $crate::status!($status_code, $message, true)
37 };
38 ($status_code:expr, $message:expr, $expose:expr) => {
39 $crate::Status::new($status_code, $message, $expose)
40 };
41}
42
43/// Throw an `Err(Status)`.
44///
45/// - `throw!(status_code)` will be expanded to `throw!(status_code, "")`
46/// - `throw!(status_code, message)` will be expanded to `throw!(status_code, message, true)`
47/// - `throw!(status_code, message, expose)` will be expanded to `return Err(Status::new(status_code, message, expose));`
48///
49/// ### Example
50/// ```rust
51/// use roa_core::{App, Context, Next, Result, throw};
52/// use roa_core::http::StatusCode;
53///
54/// let app = App::new().gate(gate).end(end);
55/// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {
56/// next.await?; // throw
57/// unreachable!();
58/// ctx.resp.status = StatusCode::OK;
59/// Ok(())
60/// }
61///
62/// async fn end(ctx: &mut Context) -> Result {
63/// throw!(StatusCode::IM_A_TEAPOT, "I'm a teapot!"); // throw
64/// unreachable!()
65/// }
66/// ```
67#[macro_export]
68macro_rules! throw {
69 ($status_code:expr) => {
70 return core::result::Result::Err($crate::status!($status_code))
71 };
72 ($status_code:expr, $message:expr) => {
73 return core::result::Result::Err($crate::status!($status_code, $message))
74 };
75 ($status_code:expr, $message:expr, $expose:expr) => {
76 return core::result::Result::Err($crate::status!($status_code, $message, $expose))
77 };
78}
79
80/// The `Status` of roa.
81#[derive(Debug, Clone, Eq, PartialEq)]
82pub struct Status {
83 /// StatusCode will be responded to client if Error is thrown by the top middleware.
84 ///
85 /// ### Example
86 /// ```rust
87 /// use roa_core::{App, Context, Next, Result, MiddlewareExt, throw};
88 /// use roa_core::http::StatusCode;
89 ///
90 /// let app = App::new().gate(gate).end(end);
91 /// async fn gate(ctx: &mut Context, next: Next<'_>) -> Result {
92 /// ctx.resp.status = StatusCode::OK;
93 /// next.await // not caught
94 /// }
95 ///
96 /// async fn end(ctx: &mut Context) -> Result {
97 /// throw!(StatusCode::IM_A_TEAPOT, "I'm a teapot!"); // throw
98 /// unreachable!()
99 /// }
100 /// ```
101 pub status_code: StatusCode,
102
103 /// Data will be written to response body if self.expose is true.
104 /// StatusCode will be responded to client if Error is thrown by the top middleware.
105 ///
106 /// ### Example
107 /// ```rust
108 /// use roa_core::{App, Context, Result, Status};
109 /// use roa_core::http::StatusCode;
110 ///
111 /// let app = App::new().end(end);
112 ///
113 /// async fn end(ctx: &mut Context) -> Result {
114 /// Err(Status::new(StatusCode::IM_A_TEAPOT, "I'm a teapot!", false)) // message won't be exposed to user.
115 /// }
116 ///
117 /// ```
118 pub message: String,
119
120 /// if message exposed.
121 pub expose: bool,
122}
123
124impl Status {
125 /// Construct an error.
126 #[inline]
127 pub fn new(status_code: StatusCode, message: impl ToString, expose: bool) -> Self {
128 Self {
129 status_code,
130 message: message.to_string(),
131 expose,
132 }
133 }
134}
135
136impl<E> From<E> for Status
137where
138 E: std::error::Error,
139{
140 #[inline]
141 fn from(err: E) -> Self {
142 Self::new(StatusCode::INTERNAL_SERVER_ERROR, err, false)
143 }
144}
145
146impl Display for Status {
147 #[inline]
148 fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), std::fmt::Error> {
149 f.write_str(&format!("{}: {}", self.status_code, self.message))
150 }
151}