1 // Copyright 2021 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_MAP_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_MAP_H
17
18 #include <grpc/support/port_platform.h>
19 #include <stddef.h>
20
21 #include <tuple>
22 #include <utility>
23
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 #include "absl/strings/str_cat.h"
27 #include "src/core/lib/promise/detail/promise_like.h"
28 #include "src/core/lib/promise/poll.h"
29
30 namespace grpc_core {
31
32 namespace promise_detail {
33
34 // Implementation of mapping combinator - use this via the free function below!
35 // Promise is the type of promise to poll on, Fn is a function that takes the
36 // result of Promise and maps it to some new type.
37 template <typename Promise, typename Fn, typename SfinaeVoid = void>
38 class Map;
39
40 template <typename Promise, typename Fn>
41 class Map<Promise, Fn,
42 absl::enable_if_t<!std::is_void<
43 #if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
44 (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
45 std::invoke_result_t<Fn, typename PromiseLike<Promise>::Result>
46 #else
47 typename std::result_of<Fn(
48 typename PromiseLike<Promise>::Result)>::type
49 #endif
50 >::value>> {
51 public:
Map(Promise promise,Fn fn)52 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Map(Promise promise, Fn fn)
53 : promise_(std::move(promise)), fn_(std::move(fn)) {}
54
55 Map(const Map&) = delete;
56 Map& operator=(const Map&) = delete;
57 // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
58 Map(Map&& other) = default;
59 // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
60 Map& operator=(Map&& other) = default;
61
62 using PromiseResult = typename PromiseLike<Promise>::Result;
63 using Result =
64 RemoveCVRef<decltype(std::declval<Fn>()(std::declval<PromiseResult>()))>;
65
operator()66 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll<Result> operator()() {
67 Poll<PromiseResult> r = promise_();
68 if (auto* p = r.value_if_ready()) {
69 return fn_(std::move(*p));
70 }
71 return Pending();
72 }
73
74 private:
75 PromiseLike<Promise> promise_;
76 Fn fn_;
77 };
78
79 template <typename Promise, typename Fn>
80 class Map<Promise, Fn,
81 absl::enable_if_t<std::is_void<
82 #if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
83 (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
84 std::invoke_result_t<Fn, typename PromiseLike<Promise>::Result>
85 #else
86 typename std::result_of<Fn(
87 typename PromiseLike<Promise>::Result)>::type
88 #endif
89 >::value>> {
90 public:
Map(Promise promise,Fn fn)91 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Map(Promise promise, Fn fn)
92 : promise_(std::move(promise)), fn_(std::move(fn)) {}
93
94 Map(const Map&) = delete;
95 Map& operator=(const Map&) = delete;
96 // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
97 Map(Map&& other) = default;
98 // NOLINTNEXTLINE(performance-noexcept-move-constructor): clang6 bug
99 Map& operator=(Map&& other) = default;
100
101 using PromiseResult = typename PromiseLike<Promise>::Result;
102 using Result = Empty;
103
operator()104 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll<Result> operator()() {
105 Poll<PromiseResult> r = promise_();
106 if (auto* p = r.value_if_ready()) {
107 fn_(std::move(*p));
108 return Empty{};
109 }
110 return Pending();
111 }
112
113 private:
114 PromiseLike<Promise> promise_;
115 Fn fn_;
116 };
117
118 } // namespace promise_detail
119
120 // Mapping combinator.
121 // Takes a promise, and a synchronous function to mutate its result, and
122 // returns a promise.
123 template <typename Promise, typename Fn>
124 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline promise_detail::Map<Promise, Fn>
Map(Promise promise,Fn fn)125 Map(Promise promise, Fn fn) {
126 return promise_detail::Map<Promise, Fn>(std::move(promise), std::move(fn));
127 }
128
129 // Maps a promise to a new promise that returns a tuple of the original result
130 // and a bool indicating whether there was ever a Pending{} value observed from
131 // polling.
132 template <typename Promise>
CheckDelayed(Promise promise)133 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline auto CheckDelayed(Promise promise) {
134 using P = promise_detail::PromiseLike<Promise>;
135 return [delayed = false, promise = P(std::move(promise))]() mutable
136 -> Poll<std::tuple<typename P::Result, bool>> {
137 auto r = promise();
138 if (r.pending()) {
139 delayed = true;
140 return Pending{};
141 }
142 return std::make_tuple(std::move(r.value()), delayed);
143 };
144 }
145
146 // Callable that takes a tuple and returns one element
147 template <size_t kElem>
148 struct JustElem {
149 template <typename... A>
150 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()(std::tuple<A...>&& t)
151 const -> decltype(std::get<kElem>(std::forward<std::tuple<A...>>(t))) {
152 return std::get<kElem>(std::forward<std::tuple<A...>>(t));
153 }
154 template <typename... A>
155 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto operator()(
156 const std::tuple<A...>& t) const -> decltype(std::get<kElem>(t)) {
157 return std::get<kElem>(t);
158 }
159 };
160
161 namespace promise_detail {
162 template <typename Fn>
163 class MapError {
164 public:
MapError(Fn fn)165 explicit MapError(Fn fn) : fn_(std::move(fn)) {}
operator()166 absl::Status operator()(absl::Status status) {
167 if (status.ok()) return status;
168 return fn_(std::move(status));
169 }
170 template <typename T>
operator()171 absl::StatusOr<T> operator()(absl::StatusOr<T> status) {
172 if (status.ok()) return status;
173 return fn_(std::move(status.status()));
174 }
175
176 private:
177 Fn fn_;
178 };
179 } // namespace promise_detail
180
181 // Map status->better status in the case of errors
182 template <typename Promise, typename Fn>
MapErrors(Promise promise,Fn fn)183 auto MapErrors(Promise promise, Fn fn) {
184 return Map(std::move(promise), promise_detail::MapError<Fn>(std::move(fn)));
185 }
186
187 // Simple mapper to add a prefix to the message of an error
188 template <typename Promise>
AddErrorPrefix(absl::string_view prefix,Promise promise)189 auto AddErrorPrefix(absl::string_view prefix, Promise promise) {
190 return MapErrors(std::move(promise), [prefix](absl::Status status) {
191 absl::Status out(status.code(), absl::StrCat(prefix, status.message()));
192 status.ForEachPayload(
193 [&out](absl::string_view name, const absl::Cord& value) {
194 out.SetPayload(name, value);
195 });
196 return out;
197 });
198 }
199
200 } // namespace grpc_core
201
202 #endif // GRPC_SRC_CORE_LIB_PROMISE_MAP_H
203