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_SWITCH_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_SWITCH_H
17
18 #include <grpc/support/port_platform.h>
19
20 #include <memory>
21 #include <utility>
22
23 #include "src/core/lib/promise/detail/promise_factory.h"
24 #include "src/core/lib/promise/detail/promise_variant.h"
25 #include "src/core/lib/promise/if.h"
26 #include "src/core/util/crash.h"
27
28 namespace grpc_core {
29
30 namespace promise_detail {
31 template <typename D, D discriminator, typename F>
32 struct Case {
33 using Factory = OncePromiseFactory<void, F>;
CaseCase34 explicit Case(F f) : factory(std::move(f)) {}
35 Factory factory;
MatchesCase36 static bool Matches(D value) { return value == discriminator; }
37 };
38
39 template <typename F>
40 struct Default {
41 using Factory = OncePromiseFactory<void, F>;
DefaultDefault42 explicit Default(F f) : factory(std::move(f)) {}
43 Factory factory;
44 };
45
46 template <typename Promise, typename D, typename F>
ConstructSwitchPromise(D,Default<F> & def)47 Promise ConstructSwitchPromise(D, Default<F>& def) {
48 return def.factory.Make();
49 }
50
51 template <typename Promise, typename D, typename Case, typename... OtherCases>
ConstructSwitchPromise(D discriminator,Case & c,OtherCases &...cs)52 Promise ConstructSwitchPromise(D discriminator, Case& c, OtherCases&... cs) {
53 if (Case::Matches(discriminator)) return c.factory.Make();
54 return ConstructSwitchPromise<Promise>(discriminator, cs...);
55 }
56
57 template <typename D, typename... Cases>
SwitchImpl(D discriminator,Cases &...cases)58 auto SwitchImpl(D discriminator, Cases&... cases) {
59 using Promise = absl::variant<typename Cases::Factory::Promise...>;
60 return PromiseVariant<Promise>(
61 ConstructSwitchPromise<Promise>(discriminator, cases...));
62 }
63
64 } // namespace promise_detail
65
66 // TODO(ctiller): when we have C++17, make this
67 // template <auto D, typename PromiseFactory>.
68 // (this way we don't need to list the type on /every/ case)
69 template <typename D, D discriminator, typename PromiseFactory>
Case(PromiseFactory f)70 auto Case(PromiseFactory f) {
71 return promise_detail::Case<D, discriminator, PromiseFactory>{std::move(f)};
72 }
73
74 template <typename PromiseFactory>
Default(PromiseFactory f)75 auto Default(PromiseFactory f) {
76 return promise_detail::Default<PromiseFactory>{std::move(f)};
77 }
78
79 // Given a list of cases that result in promise factories, return a single
80 // promise chosen by the discriminator (the first argument of this function).
81 // e.g.:
82 // Switch(1, Case<1>([] { return 43; }), Case<2>([] { return 44; }))
83 // resolves to 43.
84 // TODO(ctiller): consider writing a code-generator like we do for seq/join
85 // so that this lowers into a C switch statement.
86 template <typename D, typename... C>
Switch(D discriminator,C...cases)87 auto Switch(D discriminator, C... cases) {
88 return promise_detail::SwitchImpl(discriminator, cases...);
89 }
90
91 } // namespace grpc_core
92
93 #endif // GRPC_SRC_CORE_LIB_PROMISE_SWITCH_H
94