scufflecloud_core/
std_ext.rs1use std::fmt::Display;
2
3use tonic_types::{ErrorDetails, StatusExt};
4
5pub(crate) trait DisplayExt: Sized {
6 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> tonic::Status;
7
8 fn into_tonic_internal_err(self, msg: &str) -> tonic::Status {
9 self.into_tonic_err(tonic::Code::Internal, msg, ErrorDetails::new())
10 }
11
12 fn into_tonic_err_with_field_violation(self, field: &str, msg: &str) -> tonic::Status {
13 self.into_tonic_err(
14 tonic::Code::InvalidArgument,
15 format!("{field}: {msg}").as_str(),
16 ErrorDetails::with_bad_request_violation(field, msg),
17 )
18 }
19}
20
21impl<D> DisplayExt for D
22where
23 D: Display,
24{
25 fn into_tonic_err(self, code: tonic::Code, msg: &str, mut details: ErrorDetails) -> tonic::Status {
26 tracing::error!(err = %self, "{}", msg);
27 details.set_debug_info(vec![], self.to_string());
28 tonic::Status::with_error_details(code, msg, details)
29 }
30}
31
32pub(crate) trait ResultExt<T>: Sized {
33 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status>;
34
35 fn into_tonic_internal_err(self, msg: &str) -> Result<T, tonic::Status> {
36 self.into_tonic_err(tonic::Code::Internal, msg, ErrorDetails::new())
37 }
38
39 fn into_tonic_err_with_field_violation(self, field: &str, msg: &str) -> Result<T, tonic::Status> {
40 self.into_tonic_err(
41 tonic::Code::InvalidArgument,
42 format!("{field}: {msg}").as_str(),
43 ErrorDetails::with_bad_request_violation(field, msg),
44 )
45 }
46}
47
48impl<T, E> ResultExt<T> for Result<T, E>
49where
50 E: DisplayExt,
51{
52 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status> {
53 match self {
54 Ok(value) => Ok(value),
55 Err(e) => Err(e.into_tonic_err(code, msg, details)),
56 }
57 }
58}
59
60pub(crate) trait OptionExt<T>: Sized {
61 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status>;
62
63 fn into_tonic_not_found(self, msg: &str) -> Result<T, tonic::Status> {
64 self.into_tonic_err(tonic::Code::NotFound, msg, ErrorDetails::new())
65 }
66
67 fn into_tonic_internal_err(self, msg: &str) -> Result<T, tonic::Status> {
68 self.into_tonic_err(tonic::Code::Internal, msg, ErrorDetails::new())
69 }
70
71 fn require(self, field: &str) -> Result<T, tonic::Status> {
72 self.into_tonic_err(
73 tonic::Code::InvalidArgument,
74 format!("missing {field}").as_str(),
75 tonic_types::ErrorDetails::with_bad_request_violation(field, "not set"),
76 )
77 }
78}
79
80impl<T> OptionExt<T> for Option<T> {
81 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status> {
82 self.ok_or_else(|| {
83 tracing::error!("{}", msg);
84 tonic::Status::with_error_details(code, msg, details)
85 })
86 }
87}