ntex_grpc/server/
error.rs1use ntex_http::{HeaderMap, HeaderValue};
2
3use super::Response;
4use crate::{DecodeError, GrpcStatus};
5
6#[derive(thiserror::Error, Clone, Debug)]
14#[error("{status:?}: {message:?}")]
15pub struct ServerError {
16 pub(crate) status: GrpcStatus,
17 pub(crate) message: HeaderValue,
18 pub(crate) headers: HeaderMap,
19}
20
21impl ServerError {
22 pub fn new(status: GrpcStatus, message: HeaderValue, headers: Option<HeaderMap>) -> Self {
29 Self {
30 status,
31 message,
32 headers: headers.unwrap_or_default(),
33 }
34 }
35}
36
37impl From<DecodeError> for ServerError {
38 fn from(_: DecodeError) -> Self {
39 Self::new(
40 GrpcStatus::InvalidArgument,
41 HeaderValue::from_static("Cannot decode grpc message"),
42 None,
43 )
44 }
45}
46
47pub trait MethodResult<T> {
57 fn into_response(self) -> Result<Response<T>, ServerError>;
63}
64
65impl<T> MethodResult<T> for T {
66 #[inline]
67 fn into_response(self) -> Result<Response<T>, ServerError> {
68 Ok(Response::new(self))
69 }
70}
71
72impl<T, E: Into<T>> MethodResult<T> for Result<T, E> {
73 #[inline]
74 fn into_response(self) -> Result<Response<T>, ServerError> {
75 match self {
76 Ok(res) => Ok(Response::new(res)),
77 Err(e) => Ok(Response::new(e.into())),
78 }
79 }
80}
81
82impl<T> MethodResult<T> for Response<T> {
83 #[inline]
84 fn into_response(self) -> Result<Response<T>, ServerError> {
85 Ok(self)
86 }
87}
88
89impl<T, E: Into<ServerError>> MethodResult<T> for Result<Response<T>, E> {
90 #[inline]
91 fn into_response(self) -> Result<Response<T>, ServerError> {
92 self.map_err(Into::into)
93 }
94}
95
96pub trait StreamResult {
102 fn into_result(self) -> Result<(), ServerError>;
108}
109
110impl StreamResult for () {
111 #[inline]
112 fn into_result(self) -> Result<(), ServerError> {
113 Ok(())
114 }
115}
116
117impl<E: Into<ServerError>> StreamResult for Result<(), E> {
118 #[inline]
119 fn into_result(self) -> Result<(), ServerError> {
120 self.map_err(Into::into)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use ntex_http::HeaderName;
127
128 use super::*;
129
130 #[test]
131 fn server_error() {
132 let err = ServerError::new(GrpcStatus::NotFound, HeaderValue::from_static("nope"), None);
133 assert_eq!(err.status, GrpcStatus::NotFound);
134 assert_eq!(err.message, HeaderValue::from_static("nope"));
135 assert!(err.headers.is_empty());
136
137 let text = err.to_string();
138 assert!(text.contains("NotFound"), "{text}");
139 assert!(text.contains("nope"), "{text}");
140 assert!(format!("{err:?}").contains("NotFound"));
141 }
142
143 #[test]
144 fn server_error_with_headers() {
145 let mut headers = HeaderMap::new();
146 headers.insert(
147 HeaderName::from_static("x-test"),
148 HeaderValue::from_static("1"),
149 );
150
151 let err = ServerError::new(
152 GrpcStatus::Internal,
153 HeaderValue::from_static("boom"),
154 Some(headers),
155 );
156 let err = err.clone();
157 assert_eq!(err.status, GrpcStatus::Internal);
158 assert_eq!(
159 err.headers.get("x-test").unwrap(),
160 &HeaderValue::from_static("1")
161 );
162 }
163
164 #[test]
165 fn from_decode_error() {
166 let err = ServerError::from(DecodeError::new("bad data"));
167 assert_eq!(err.status, GrpcStatus::InvalidArgument);
168 assert_eq!(
169 err.message,
170 HeaderValue::from_static("Cannot decode grpc message")
171 );
172 assert!(err.headers.is_empty());
173 }
174
175 #[test]
185 fn method_result_conversion() {
186 fn reply<R: MethodResult<u32>>(res: R) -> u32 {
187 let res = res.into_response().unwrap();
188 assert!(res.headers.is_empty());
189 res.message
190 }
191
192 assert_eq!(reply(5), 5);
193 assert_eq!(reply(Ok::<u32, u8>(7)), 7);
194 assert_eq!(reply(Err::<u32, u8>(3)), 3);
195
196 let res = Response {
197 message: 9_u32,
198 headers: vec![(
199 HeaderName::from_static("x-test"),
200 HeaderValue::from_static("1"),
201 )],
202 };
203 let res = MethodResult::<u32>::into_response(res).unwrap();
204 assert_eq!(res.message, 9);
205 assert_eq!(res.headers.len(), 1);
206
207 let ok: Result<Response<u32>, ServerError> = Ok(Response::new(4));
208 assert_eq!(MethodResult::<u32>::into_response(ok).unwrap().message, 4);
209
210 let err: Result<Response<u32>, ServerError> = Err(ServerError::new(
211 GrpcStatus::NotFound,
212 HeaderValue::from_static("nope"),
213 None,
214 ));
215 let err = MethodResult::<u32>::into_response(err).err().unwrap();
216 assert_eq!(err.status, GrpcStatus::NotFound);
217 }
218}