pdk_classy/grpc/
status.rs

1// Copyright (c) 2025, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5use std::fmt::Display;
6
7const OK: u32 = 0;
8const CANCELLED: u32 = 1;
9const UNKNOWN: u32 = 2;
10const INVALID_ARGUMENT: u32 = 3;
11const DEADLINE_EXCEEDED: u32 = 4;
12const NOT_FOUND: u32 = 5;
13const ALREADY_EXISTS: u32 = 6;
14const PERMISSION_DENIED: u32 = 7;
15const RESOURCE_EXHAUSTED: u32 = 8;
16const FAILED_PRECONDITION: u32 = 9;
17const ABORTED: u32 = 10;
18const OUT_OF_RANGE: u32 = 11;
19const UNIMPLEMENTED: u32 = 12;
20const INTERNAL: u32 = 13;
21const UNAVAILABLE: u32 = 14;
22const DATA_LOSS: u32 = 15;
23const UNAUTHENTICATED: u32 = 16;
24
25/// The possible status that a gRPC endpoint can return.
26#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
27#[repr(u32)]
28pub enum GrpcStatusCode {
29    /// The operation completed successfully.
30    Ok = OK,
31
32    /// The operation was cancelled.
33    Cancelled = CANCELLED,
34
35    /// Unknown error.
36    Unknown = UNKNOWN,
37
38    /// Client specified an invalid argument.
39    InvalidArgument = INVALID_ARGUMENT,
40
41    /// Deadline expired before operation could complete.
42    DeadlineExceeded = DEADLINE_EXCEEDED,
43
44    /// Some requested entity was not found.
45    NotFound = NOT_FOUND,
46
47    /// Some entity that we attempted to create already exists.
48    AlreadyExists = ALREADY_EXISTS,
49
50    /// The caller does not have permission to execute the specified operation.
51    PermissionDenied = PERMISSION_DENIED,
52
53    /// Some resource has been exhausted.
54    ResourceExhausted = RESOURCE_EXHAUSTED,
55
56    /// The system is not in a state required for the operation's execution.
57    FailedPrecondition = FAILED_PRECONDITION,
58
59    /// The operation was aborted.
60    Aborted = ABORTED,
61
62    /// Operation was attempted past the valid range.
63    OutOfRange = OUT_OF_RANGE,
64
65    /// Operation is not implemented or not supported.
66    Unimplemented = UNIMPLEMENTED,
67
68    /// Internal error.
69    Internal = INTERNAL,
70
71    /// The service is currently unavailable.
72    Unavailable = UNAVAILABLE,
73
74    /// Unrecoverable data loss or corruption.
75    DataLoss = DATA_LOSS,
76
77    /// The request does not have valid authentication credentials
78    Unauthenticated = UNAUTHENTICATED,
79}
80
81impl GrpcStatusCode {
82    pub(super) fn from_u32(code: u32) -> Self {
83        match code {
84            OK => Self::Ok,
85            CANCELLED => Self::Cancelled,
86            UNKNOWN => Self::Unknown,
87            INVALID_ARGUMENT => Self::InvalidArgument,
88            DEADLINE_EXCEEDED => Self::DeadlineExceeded,
89            NOT_FOUND => Self::NotFound,
90            ALREADY_EXISTS => Self::AlreadyExists,
91            PERMISSION_DENIED => Self::PermissionDenied,
92            RESOURCE_EXHAUSTED => Self::ResourceExhausted,
93            FAILED_PRECONDITION => Self::FailedPrecondition,
94            ABORTED => Self::Aborted,
95            OUT_OF_RANGE => Self::OutOfRange,
96            UNIMPLEMENTED => Self::Unimplemented,
97            INTERNAL => Self::Internal,
98            UNAVAILABLE => Self::Unavailable,
99            DATA_LOSS => Self::DataLoss,
100            UNAUTHENTICATED => Self::Unauthenticated,
101            _ => Self::Unknown,
102        }
103    }
104
105    /// Returns the status numeric representation.
106    pub fn code(&self) -> u32 {
107        *self as u32
108    }
109
110    /// Returns the status mnemonic label.
111    pub fn label(&self) -> &'static str {
112        match *self {
113            GrpcStatusCode::Ok => "OK",
114            GrpcStatusCode::Cancelled => "CANCELLED",
115            GrpcStatusCode::Unknown => "UNKNOWN",
116            GrpcStatusCode::InvalidArgument => "INVALID_ARGUMENT",
117            GrpcStatusCode::DeadlineExceeded => "DEADLINE_EXCEEDED",
118            GrpcStatusCode::NotFound => "NOT_FOUND",
119            GrpcStatusCode::AlreadyExists => "ALREADY_EXISTS",
120            GrpcStatusCode::PermissionDenied => "PERMISSION_DENIED",
121            GrpcStatusCode::ResourceExhausted => "RESOURCE_EXHAUSTED",
122            GrpcStatusCode::FailedPrecondition => "FAILED_PRECONDITION",
123            GrpcStatusCode::Aborted => "ABORTED",
124            GrpcStatusCode::OutOfRange => "OUT_OF_RANGE",
125            GrpcStatusCode::Unimplemented => "UNIMPLEMENTED",
126            GrpcStatusCode::Internal => "INTERNAL",
127            GrpcStatusCode::Unavailable => "UNAVAILABLE",
128            GrpcStatusCode::DataLoss => "DATA_LOSS",
129            GrpcStatusCode::Unauthenticated => "UNAUTHENTICATED",
130        }
131    }
132}
133
134/// Represents a status returned from a gRPC endpoint.
135#[derive(Debug)]
136pub struct GrpcStatus {
137    code: GrpcStatusCode,
138    message: Option<String>,
139}
140
141impl GrpcStatus {
142    /// Creates a gRPC status.
143    pub fn new(status: GrpcStatusCode, message: Option<String>) -> Self {
144        Self {
145            code: status,
146            message,
147        }
148    }
149
150    /// Creates a gRPC status with empty message.
151    pub fn from_code(status: GrpcStatusCode) -> Self {
152        Self {
153            code: status,
154            message: None,
155        }
156    }
157
158    /// Returns the gRPC status code.
159    pub fn code(&self) -> GrpcStatusCode {
160        self.code
161    }
162
163    /// Returns the gRPC status message.
164    pub fn message(&self) -> Option<&str> {
165        self.message.as_deref()
166    }
167}
168
169impl Display for GrpcStatus {
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        let code = self.code.label();
172        if let Some(message) = self.message.as_deref() {
173            write!(f, "[{code}] {message}")
174        } else {
175            write!(f, "{code}")
176        }
177    }
178}