lightning_signer/util/
status.rs

1use crate::prelude::*;
2use core::fmt;
3
4#[cfg(feature = "use_backtrace")]
5use backtrace::Backtrace;
6use log::*;
7
8use crate::policy::error::{ValidationError, ValidationErrorKind};
9
10/// gRPC compatible error status
11#[derive(Clone)]
12pub struct Status {
13    /// The gRPC status code, found in the `grpc-status` header.
14    code: Code,
15    /// A relevant error message, found in the `grpc-message` header.
16    message: String,
17}
18
19/// gRPC compatible error status code.
20///
21/// Must be a subset of [tonic::Code], or the conversion will panic.
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum Code {
24    /// The operation completed successfully.
25    Ok = 0,
26
27    /// Client specified an invalid argument.
28    InvalidArgument = 3,
29
30    /// The system is not in a state required for the operation’s execution.
31    FailedPrecondition = 9,
32
33    /// Internal error.
34    Internal = 13,
35
36    /// Temporary error.
37    Temporary = 14,
38}
39
40impl Status {
41    /// Create a new `Status` with the associated code and message.
42    pub fn new(code: Code, message: impl Into<String>) -> Self {
43        Status { code, message: message.into() }
44    }
45
46    /// Get the gRPC `Code` of this `Status`.
47    pub fn code(&self) -> Code {
48        self.code
49    }
50
51    /// Get the text error message of this `Status`.
52    pub fn message(&self) -> &str {
53        &self.message
54    }
55
56    /// Construct an invalid argument status
57    pub fn invalid_argument(message: impl Into<String>) -> Status {
58        Self::new(Code::InvalidArgument, message)
59    }
60
61    /// Construct a failed precondition status, used for policy violation
62    pub fn failed_precondition(message: impl Into<String>) -> Status {
63        Self::new(Code::FailedPrecondition, message)
64    }
65
66    /// Construct an internal error status
67    pub fn internal(message: impl Into<String>) -> Status {
68        Self::new(Code::Internal, message)
69    }
70
71    /// Construct a temporary error status
72    pub fn temporary(message: impl Into<String>) -> Status {
73        Self::new(Code::Temporary, message)
74    }
75}
76
77impl fmt::Debug for Status {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        // A manual impl to reduce the noise of frequently empty fields.
80        let mut builder = f.debug_struct("Status");
81
82        builder.field("code", &self.code);
83
84        if !self.message.is_empty() {
85            builder.field("message", &self.message);
86        }
87
88        builder.finish()
89    }
90}
91
92impl fmt::Display for Status {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        write!(f, "status: {:?}, message: {:?}", self.code(), self.message())
95    }
96}
97
98#[cfg(feature = "grpc")]
99impl std::error::Error for Status {}
100
101#[cfg(feature = "grpc")]
102use core::convert::TryInto;
103
104#[cfg(feature = "grpc")]
105impl From<Status> for tonic::Status {
106    fn from(s: Status) -> Self {
107        let code = s.code() as i32;
108        // this is safe because our Status::Code enum is a subset of tonic::Code
109        tonic::Status::new(code.try_into().unwrap(), s.message())
110    }
111}
112
113/// An invalid argument was detected
114pub fn invalid_argument(msg: impl Into<String>) -> Status {
115    let s = msg.into();
116    error!("INVALID ARGUMENT: {}", &s);
117    #[cfg(feature = "use_backtrace")]
118    error!("BACKTRACE:\n{:?}", Backtrace::new());
119    Status::invalid_argument(s)
120}
121
122pub(crate) fn internal_error(msg: impl Into<String>) -> Status {
123    let s = msg.into();
124    error!("INTERNAL ERROR: {}", &s);
125    #[cfg(feature = "use_backtrace")]
126    error!("BACKTRACE:\n{:?}", Backtrace::new());
127    Status::internal(s)
128}
129
130#[allow(unused)]
131pub(crate) fn failed_precondition(msg: impl Into<String>) -> Status {
132    let s = msg.into();
133    error!("FAILED PRECONDITION: {}", &s);
134    // Skip backtrace since ValidationError handled already ...
135    Status::failed_precondition(s)
136}
137
138impl From<ValidationError> for Status {
139    fn from(ve: ValidationError) -> Self {
140        let res = match ve.kind {
141            ValidationErrorKind::TemporaryPolicy(ref s) => {
142                warn!("TEMPORARY POLICY ERROR: {}", s);
143                #[cfg(feature = "use_backtrace")]
144                warn!("BACKTRACE:\n{:?}", &ve.resolved_backtrace());
145                Status::temporary(s)
146            }
147            _ => {
148                let s: String = ve.clone().into();
149                error!("FAILED PRECONDITION: {}", &s);
150                #[cfg(feature = "use_backtrace")]
151                error!("BACKTRACE:\n{:?}", &ve.resolved_backtrace());
152                Status::failed_precondition(s)
153            }
154        };
155        res
156    }
157}