• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://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, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include "pw_assert/assert.h"
17 #include "pw_status/status.h"
18 
19 namespace pw {
20 namespace rpc {
21 
22 // A `SynchronousCallResult<Response>` is an object that contains the result of
23 // a `SynchronousCall`. When synchronous calls are made, errors could occur for
24 // multiple reasons. There could have been an error at either the RPC layer or
25 // Server; or the client-specified timeout might have occurred. A user can query
26 // this object to determine what type of error occurred, so that they can handle
27 // it appropriately. If the server responded, the response() and dereference
28 // operators will provide access to the Response.
29 //
30 // Example:
31 //
32 //   SynchronousCallResult<MyResponse> result =
33 //     SynchronousCallFor<MyService::Method>(client, request, timeout);
34 //   if (result.is_rpc_error()) {
35 //     ShutdownClient(client);
36 //   } else if (result.is_timeout()) {
37 //     RetryCall(client, request);
38 //   } else if (result.is_server_error()) {
39 //     return result.status();
40 //   }
41 //   PW_ASSERT(result.ok());
42 //   return std::move(result).response();
43 //
44 // For some RPCs, the server could have responded with a non-Ok Status but with
45 // a valid Response object. For example, if the server was ran out of space in a
46 // buffer, it might return a Status of ResourceExhausted, but the response
47 // contains as much data as could fit. In this situation, users should be
48 // careful not to treat the error as fatal.
49 //
50 // Example:
51 //
52 //   SynchronousCallResult<BufferResponse> result =
53 //     SynchronousCall<MyService::Read>(client, request);
54 //   if (result.is_rpc_error()) {
55 //     ShutdownClient(client);
56 //   }
57 //   PW_ASSERT(result.is_server_response());
58 //   HandleServerResponse(result.status(), result.response());
59 //
60 
61 namespace internal {
62 enum class SynchronousCallStatus {
63   kInvalid,
64   kTimeout,
65   kRpc,
66   kServer,
67 };
68 }  // namespace internal
69 
70 template <typename Response>
71 class SynchronousCallResult {
72  public:
73   // Error Constructors
74   constexpr static SynchronousCallResult Timeout();
75   constexpr static SynchronousCallResult RpcError(Status status);
76 
77   // Server Response Constructor
SynchronousCallResult(Status status,Response response)78   constexpr explicit SynchronousCallResult(Status status, Response response)
79       : call_status_(internal::SynchronousCallStatus::kServer),
80         status_(status),
81         response_(std::move(response)) {}
82 
83   constexpr SynchronousCallResult() = default;
84   ~SynchronousCallResult() = default;
85 
86   // Copyable if `Response` is copyable.
87   constexpr SynchronousCallResult(const SynchronousCallResult&) = default;
88   constexpr SynchronousCallResult& operator=(const SynchronousCallResult&) =
89       default;
90 
91   // Movable if `Response` is movable.
92   constexpr SynchronousCallResult(SynchronousCallResult&&) = default;
93   constexpr SynchronousCallResult& operator=(SynchronousCallResult&&) = default;
94 
95   // Returns true if there was a timeout, an rpc error or the server returned a
96   // non-Ok status.
97   [[nodiscard]] constexpr bool is_error() const;
98 
99   // Returns true if the server returned a response with an Ok status.
100   [[nodiscard]] constexpr bool ok() const;
101 
102   // Returns true if the server responded with a non-Ok status.
103   [[nodiscard]] constexpr bool is_server_error() const;
104 
105   [[nodiscard]] constexpr bool is_timeout() const;
106   [[nodiscard]] constexpr bool is_rpc_error() const;
107   [[nodiscard]] constexpr bool is_server_response() const;
108 
109   [[nodiscard]] constexpr Status status() const;
110 
111   // SynchronousCallResult<Response>::response()
112   // SynchronousCallResult<Response>::operator*()
113   // SynchronousCallResult<Response>::operator->()
114   //
115   // Accessors to the held value if `this->is_server_response()`. Otherwise,
116   // terminates the process.
117   constexpr const Response& response() const&;
118   constexpr Response& response() &;
119   constexpr const Response&& response() const&&;
120   constexpr Response&& response() &&;
121 
122   constexpr const Response& operator*() const&;
123   constexpr Response& operator*() &;
124   constexpr const Response&& operator*() const&&;
125   constexpr Response&& operator*() &&;
126 
127   constexpr const Response* operator->() const;
128   constexpr Response* operator->();
129 
130  private:
131   // This constructor is private to protect against invariants that might occur
132   // when constructing with a SynchronousCallStatus.
SynchronousCallResult(internal::SynchronousCallStatus call_status,Status status)133   constexpr explicit SynchronousCallResult(
134       internal::SynchronousCallStatus call_status, Status status)
135       : call_status_(call_status), status_(status) {}
136 
137   internal::SynchronousCallStatus call_status_ =
138       internal::SynchronousCallStatus::kInvalid;
139   Status status_{};
140   Response response_{};
141 };
142 
143 // Implementations
144 
145 template <typename Response>
146 constexpr SynchronousCallResult<Response>
Timeout()147 SynchronousCallResult<Response>::Timeout() {
148   return SynchronousCallResult(internal::SynchronousCallStatus::kTimeout,
149                                Status::DeadlineExceeded());
150 }
151 
152 template <typename Response>
153 constexpr SynchronousCallResult<Response>
RpcError(Status status)154 SynchronousCallResult<Response>::RpcError(Status status) {
155   return SynchronousCallResult(internal::SynchronousCallStatus::kRpc, status);
156 }
157 
158 template <typename Response>
is_error()159 constexpr bool SynchronousCallResult<Response>::is_error() const {
160   return !ok();
161 }
162 
163 template <typename Response>
ok()164 constexpr bool SynchronousCallResult<Response>::ok() const {
165   return is_server_response() && status_.ok();
166 }
167 
168 template <typename Response>
is_server_error()169 constexpr bool SynchronousCallResult<Response>::is_server_error() const {
170   return is_server_response() && !status_.ok();
171 }
172 
173 template <typename Response>
is_timeout()174 constexpr bool SynchronousCallResult<Response>::is_timeout() const {
175   return call_status_ == internal::SynchronousCallStatus::kTimeout;
176 }
177 
178 template <typename Response>
is_rpc_error()179 constexpr bool SynchronousCallResult<Response>::is_rpc_error() const {
180   return call_status_ == internal::SynchronousCallStatus::kRpc;
181 }
182 
183 template <typename Response>
is_server_response()184 constexpr bool SynchronousCallResult<Response>::is_server_response() const {
185   return call_status_ == internal::SynchronousCallStatus::kServer;
186 }
187 
188 template <typename Response>
status()189 constexpr Status SynchronousCallResult<Response>::status() const {
190   PW_ASSERT(call_status_ != internal::SynchronousCallStatus::kInvalid);
191   return status_;
192 }
193 
194 template <typename Response>
response()195 constexpr const Response& SynchronousCallResult<Response>::response() const& {
196   PW_ASSERT(is_server_response());
197   return response_;
198 }
199 
200 template <typename Response>
response()201 constexpr Response& SynchronousCallResult<Response>::response() & {
202   PW_ASSERT(is_server_response());
203   return response_;
204 }
205 
206 template <typename Response>
response()207 constexpr const Response&& SynchronousCallResult<Response>::response() const&& {
208   PW_ASSERT(is_server_response());
209   return std::move(response_);
210 }
211 
212 template <typename Response>
response()213 constexpr Response&& SynchronousCallResult<Response>::response() && {
214   PW_ASSERT(is_server_response());
215   return std::move(response_);
216 }
217 
218 template <typename Response>
219 constexpr const Response& SynchronousCallResult<Response>::operator*() const& {
220   PW_ASSERT(is_server_response());
221   return response_;
222 }
223 
224 template <typename Response>
225 constexpr Response& SynchronousCallResult<Response>::operator*() & {
226   PW_ASSERT(is_server_response());
227   return response_;
228 }
229 
230 template <typename Response>
231 constexpr const Response&& SynchronousCallResult<Response>::operator*()
232     const&& {
233   PW_ASSERT(is_server_response());
234   return std::move(response_);
235 }
236 
237 template <typename Response>
238 constexpr Response&& SynchronousCallResult<Response>::operator*() && {
239   PW_ASSERT(is_server_response());
240   return std::move(response_);
241 }
242 
243 template <typename Response>
244 constexpr const Response* SynchronousCallResult<Response>::operator->() const {
245   PW_ASSERT(is_server_response());
246   return &response_;
247 }
248 
249 template <typename Response>
250 constexpr Response* SynchronousCallResult<Response>::operator->() {
251   PW_ASSERT(is_server_response());
252   return &response_;
253 }
254 
255 }  // namespace rpc
256 
257 // Conversion functions for usage with PW_TRY and PW_TRY_ASSIGN.
258 namespace internal {
259 
260 template <typename T>
ConvertToStatus(const rpc::SynchronousCallResult<T> & result)261 constexpr Status ConvertToStatus(const rpc::SynchronousCallResult<T>& result) {
262   return result.status();
263 }
264 
265 template <typename T>
ConvertToValue(rpc::SynchronousCallResult<T> & result)266 constexpr T ConvertToValue(rpc::SynchronousCallResult<T>& result) {
267   return std::move(result.response());
268 }
269 
270 }  // namespace internal
271 }  // namespace pw
272