scufflecloud_ext/
std_ext.rs1use std::fmt::Display;
2
3use tonic_types::{ErrorDetails, StatusExt};
4
5pub 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, details: ErrorDetails) -> tonic::Status {
26 fn into_tonic_err_inner(err: String, code: tonic::Code, msg: &str, mut details: ErrorDetails) -> tonic::Status {
30 tracing::error!(err = %err, "{}", msg);
31 details.set_debug_info(vec![], err);
32 tonic::Status::with_error_details(code, msg, details)
33 }
34
35 into_tonic_err_inner(self.to_string(), code, msg, details)
36 }
37}
38
39pub trait ResultExt<T>: Sized {
40 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status>;
41
42 fn into_tonic_internal_err(self, msg: &str) -> Result<T, tonic::Status> {
43 self.into_tonic_err(tonic::Code::Internal, msg, ErrorDetails::new())
44 }
45
46 fn into_tonic_err_with_field_violation(self, field: &str, msg: &str) -> Result<T, tonic::Status> {
47 self.into_tonic_err(
48 tonic::Code::InvalidArgument,
49 format!("{field}: {msg}").as_str(),
50 ErrorDetails::with_bad_request_violation(field, msg),
51 )
52 }
53}
54
55impl<T, E> ResultExt<T> for Result<T, E>
56where
57 E: DisplayExt,
58{
59 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status> {
60 match self {
61 Ok(value) => Ok(value),
62 Err(e) => Err(e.into_tonic_err(code, msg, details)),
63 }
64 }
65}
66
67pub trait OptionExt<T>: Sized {
68 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status>;
69
70 fn into_tonic_not_found(self, msg: &str) -> Result<T, tonic::Status> {
71 self.into_tonic_err(tonic::Code::NotFound, msg, ErrorDetails::new())
72 }
73
74 fn into_tonic_internal_err(self, msg: &str) -> Result<T, tonic::Status> {
75 self.into_tonic_err(tonic::Code::Internal, msg, ErrorDetails::new())
76 }
77
78 fn require(self, field: &str) -> Result<T, tonic::Status> {
79 self.into_tonic_err(
80 tonic::Code::InvalidArgument,
81 format!("missing {field}").as_str(),
82 tonic_types::ErrorDetails::with_bad_request_violation(field, "not set"),
83 )
84 }
85}
86
87impl<T> OptionExt<T> for Option<T> {
88 fn into_tonic_err(self, code: tonic::Code, msg: &str, details: ErrorDetails) -> Result<T, tonic::Status> {
89 self.ok_or_else(|| {
90 tracing::error!("{}", msg);
91 tonic::Status::with_error_details(code, msg, details)
92 })
93 }
94}