1 // Copyright 2023 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef GRPC_SRC_CORE_LIB_PROMISE_STATUS_FLAG_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_STATUS_FLAG_H
17
18 #include <grpc/support/port_platform.h>
19
20 #include <ostream>
21
22 #include "absl/log/check.h"
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/types/optional.h"
27 #include "src/core/lib/promise/detail/status.h"
28
29 namespace grpc_core {
30
31 struct Failure {
32 template <typename Sink>
AbslStringifyFailure33 friend void AbslStringify(Sink& sink, Failure) {
34 sink.Append("failed");
35 }
36 };
37 struct Success {
38 template <typename Sink>
AbslStringifySuccess39 friend void AbslStringify(Sink& sink, Success) {
40 sink.Append("ok");
41 }
42 };
43
IsStatusOk(Failure)44 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool IsStatusOk(Failure) {
45 return false;
46 }
IsStatusOk(Success)47 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool IsStatusOk(Success) {
48 return true;
49 }
50
51 template <>
52 struct StatusCastImpl<absl::Status, Success> {
53 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::Status Cast(Success) {
54 return absl::OkStatus();
55 }
56 };
57
58 template <>
59 struct StatusCastImpl<absl::Status, Success&> {
60 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::Status Cast(Success) {
61 return absl::OkStatus();
62 }
63 };
64
65 template <>
66 struct StatusCastImpl<absl::Status, const Success&> {
67 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::Status Cast(Success) {
68 return absl::OkStatus();
69 }
70 };
71
72 template <>
73 struct StatusCastImpl<absl::Status, Failure> {
74 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::Status Cast(Failure) {
75 return absl::CancelledError();
76 }
77 };
78
79 template <typename T>
80 struct StatusCastImpl<absl::StatusOr<T>, Failure> {
81 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::StatusOr<T> Cast(Failure) {
82 return absl::CancelledError();
83 }
84 };
85
86 // A boolean representing whether an operation succeeded (true) or failed
87 // (false).
88 class StatusFlag {
89 public:
90 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION StatusFlag() : value_(true) {}
91 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION explicit StatusFlag(bool value)
92 : value_(value) {}
93 // NOLINTNEXTLINE(google-explicit-constructor)
94 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION StatusFlag(Failure) : value_(false) {}
95 // NOLINTNEXTLINE(google-explicit-constructor)
96 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION StatusFlag(Success) : value_(true) {}
97
98 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool ok() const { return value_; }
99
100 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool operator==(StatusFlag other) const {
101 return value_ == other.value_;
102 }
103 std::string ToString() const { return value_ ? "ok" : "failed"; }
104
105 template <typename Sink>
106 friend void AbslStringify(Sink& sink, StatusFlag flag) {
107 if (flag.ok()) {
108 sink.Append("ok");
109 } else {
110 sink.Append("failed");
111 }
112 }
113
114 private:
115 bool value_;
116 };
117
118 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator==(StatusFlag flag,
119 Failure) {
120 return !flag.ok();
121 }
122 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator==(Failure,
123 StatusFlag flag) {
124 return !flag.ok();
125 }
126 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator==(StatusFlag flag,
127 Success) {
128 return flag.ok();
129 }
130 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator==(Success,
131 StatusFlag flag) {
132 return flag.ok();
133 }
134
135 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator!=(StatusFlag flag,
136 Failure) {
137 return flag.ok();
138 }
139 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator!=(Failure,
140 StatusFlag flag) {
141 return flag.ok();
142 }
143 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator!=(StatusFlag flag,
144 Success) {
145 return !flag.ok();
146 }
147 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool operator!=(Success,
148 StatusFlag flag) {
149 return !flag.ok();
150 }
151
152 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool IsStatusOk(
153 const StatusFlag& flag) {
154 return flag.ok();
155 }
156
157 template <>
158 struct StatusCastImpl<absl::Status, StatusFlag> {
159 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::Status Cast(
160 StatusFlag flag) {
161 return flag.ok() ? absl::OkStatus() : absl::CancelledError();
162 }
163 };
164
165 template <>
166 struct StatusCastImpl<absl::Status, StatusFlag&> {
167 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::Status Cast(
168 StatusFlag flag) {
169 return flag.ok() ? absl::OkStatus() : absl::CancelledError();
170 }
171 };
172
173 template <>
174 struct StatusCastImpl<absl::Status, const StatusFlag&> {
175 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::Status Cast(
176 StatusFlag flag) {
177 return flag.ok() ? absl::OkStatus() : absl::CancelledError();
178 }
179 };
180
181 template <>
182 struct StatusCastImpl<StatusFlag, Success> {
183 static StatusFlag Cast(Success) { return StatusFlag(true); }
184 };
185
186 template <>
187 struct StatusCastImpl<StatusFlag, Failure> {
188 static StatusFlag Cast(Failure) { return StatusFlag(false); }
189 };
190
191 template <>
192 struct FailureStatusCastImpl<StatusFlag, Failure> {
193 static StatusFlag Cast(Failure) { return StatusFlag(false); }
194 };
195
196 template <typename T>
197 struct FailureStatusCastImpl<absl::StatusOr<T>, StatusFlag> {
198 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::StatusOr<T> Cast(
199 StatusFlag flag) {
200 DCHECK(!flag.ok());
201 return absl::CancelledError();
202 }
203 };
204
205 template <typename T>
206 struct FailureStatusCastImpl<absl::StatusOr<T>, StatusFlag&> {
207 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::StatusOr<T> Cast(
208 StatusFlag flag) {
209 DCHECK(!flag.ok());
210 return absl::CancelledError();
211 }
212 };
213
214 template <typename T>
215 struct FailureStatusCastImpl<absl::StatusOr<T>, const StatusFlag&> {
216 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::StatusOr<T> Cast(
217 StatusFlag flag) {
218 DCHECK(!flag.ok());
219 return absl::CancelledError();
220 }
221 };
222
223 // A value if an operation was successful, or a failure flag if not.
224 template <typename T>
225 class ValueOrFailure {
226 public:
227 // NOLINTNEXTLINE(google-explicit-constructor)
228 ValueOrFailure(T value) : value_(std::move(value)) {}
229 // NOLINTNEXTLINE(google-explicit-constructor)
230 ValueOrFailure(Failure) {}
231 // NOLINTNEXTLINE(google-explicit-constructor)
232 ValueOrFailure(StatusFlag status) { CHECK(!status.ok()); }
233
234 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static ValueOrFailure FromOptional(
235 absl::optional<T> value) {
236 return ValueOrFailure{std::move(value)};
237 }
238
239 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool ok() const {
240 return value_.has_value();
241 }
242 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION StatusFlag status() const {
243 return StatusFlag(ok());
244 }
245
246 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION const T& value() const {
247 return value_.value();
248 }
249 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION T& value() { return value_.value(); }
250 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION const T& operator*() const {
251 return *value_;
252 }
253 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION T& operator*() { return *value_; }
254 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION const T* operator->() const {
255 return &*value_;
256 }
257 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION T* operator->() { return &*value_; }
258
259 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool operator==(
260 const ValueOrFailure& other) const {
261 return value_ == other.value_;
262 }
263
264 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool operator!=(
265 const ValueOrFailure& other) const {
266 return value_ != other.value_;
267 }
268
269 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool operator==(const T& other) const {
270 return value_ == other;
271 }
272
273 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION bool operator!=(const T& other) const {
274 return value_ != other;
275 }
276
277 template <typename Sink>
278 friend void AbslStringify(Sink& sink, const ValueOrFailure& value) {
279 if (value.ok()) {
280 sink.Append("Success(");
281 sink.Append(absl::StrCat(*value));
282 sink.Append(")");
283 } else {
284 sink.Append("Failure");
285 }
286 }
287
288 private:
289 absl::optional<T> value_;
290 };
291
292 template <typename T>
293 inline std::ostream& operator<<(std::ostream& os,
294 const ValueOrFailure<T>& value) {
295 if (value.ok()) {
296 return os << "Success(" << *value << ")";
297 } else {
298 return os << "Failure";
299 }
300 }
301
302 template <typename T>
303 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool IsStatusOk(
304 const ValueOrFailure<T>& value) {
305 return value.ok();
306 }
307
308 template <typename T>
309 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline T TakeValue(
310 ValueOrFailure<T>&& value) {
311 return std::move(value.value());
312 }
313
314 template <typename T>
315 struct StatusCastImpl<absl::StatusOr<T>, ValueOrFailure<T>> {
316 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static absl::StatusOr<T> Cast(
317 ValueOrFailure<T> value) {
318 return value.ok() ? absl::StatusOr<T>(std::move(value.value()))
319 : absl::CancelledError();
320 }
321 };
322
323 template <typename T>
324 struct StatusCastImpl<ValueOrFailure<T>, Failure> {
325 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static ValueOrFailure<T> Cast(Failure) {
326 return ValueOrFailure<T>(Failure{});
327 }
328 };
329
330 template <typename T>
331 struct StatusCastImpl<ValueOrFailure<T>, StatusFlag&> {
332 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static ValueOrFailure<T> Cast(
333 StatusFlag f) {
334 CHECK(!f.ok());
335 return ValueOrFailure<T>(Failure{});
336 }
337 };
338
339 template <typename T>
340 struct StatusCastImpl<ValueOrFailure<T>, StatusFlag> {
341 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static ValueOrFailure<T> Cast(
342 StatusFlag f) {
343 CHECK(!f.ok());
344 return ValueOrFailure<T>(Failure{});
345 }
346 };
347
348 } // namespace grpc_core
349
350 #endif // GRPC_SRC_CORE_LIB_PROMISE_STATUS_FLAG_H
351