C/C++ API Reference
Loading...
Searching...
No Matches
synchronous_call_result.h
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
19namespace pw {
20namespace 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
61namespace internal {
62enum class SynchronousCallStatus {
63 kInvalid,
64 kTimeout,
65 kRpc,
66 kServer,
67};
68} // namespace internal
69
71
72template <typename Response>
74 public:
75 // Error Constructors
76 constexpr static SynchronousCallResult Timeout();
77 constexpr static SynchronousCallResult RpcError(Status status);
78
79 // Server Response Constructor
80 constexpr explicit SynchronousCallResult(Status status, Response response)
81 : call_status_(internal::SynchronousCallStatus::kServer),
82 status_(status),
83 response_(std::move(response)) {}
84
85 constexpr SynchronousCallResult() = default;
86 ~SynchronousCallResult() = default;
87
88 // Copyable if `Response` is copyable.
89 constexpr SynchronousCallResult(const SynchronousCallResult&) = default;
90 constexpr SynchronousCallResult& operator=(const SynchronousCallResult&) =
91 default;
92
93 // Movable if `Response` is movable.
94 constexpr SynchronousCallResult(SynchronousCallResult&&) = default;
95 constexpr SynchronousCallResult& operator=(SynchronousCallResult&&) = default;
96
97 // Returns true if there was a timeout, an rpc error or the server returned a
98 // non-Ok status.
99 [[nodiscard]] constexpr bool is_error() const;
100
101 // Returns true if the server returned a response with an Ok status.
102 [[nodiscard]] constexpr bool ok() const;
103
104 // Returns true if the server responded with a non-Ok status.
105 [[nodiscard]] constexpr bool is_server_error() const;
106
107 [[nodiscard]] constexpr bool is_timeout() const;
108 [[nodiscard]] constexpr bool is_rpc_error() const;
109 [[nodiscard]] constexpr bool is_server_response() const;
110
111 [[nodiscard]] constexpr Status status() const;
112
113 // SynchronousCallResult<Response>::response()
114 // SynchronousCallResult<Response>::operator*()
115 // SynchronousCallResult<Response>::operator->()
116 //
117 // Accessors to the held value if `this->is_server_response()`. Otherwise,
118 // terminates the process.
119 constexpr const Response& response() const&;
120 constexpr Response& response() &;
121 constexpr const Response&& response() const&&;
122 constexpr Response&& response() &&;
123
124 constexpr const Response& operator*() const&;
125 constexpr Response& operator*() &;
126 constexpr const Response&& operator*() const&&;
127 constexpr Response&& operator*() &&;
128
129 constexpr const Response* operator->() const;
130 constexpr Response* operator->();
131
132 private:
133 // This constructor is private to protect against invariants that might occur
134 // when constructing with a SynchronousCallStatus.
135 constexpr explicit SynchronousCallResult(
136 internal::SynchronousCallStatus call_status, Status status)
137 : call_status_(call_status), status_(status) {}
138
139 internal::SynchronousCallStatus call_status_ =
140 internal::SynchronousCallStatus::kInvalid;
141 Status status_{};
142 Response response_{};
143};
144
146
147// Implementations
148
149template <typename Response>
152 return SynchronousCallResult(internal::SynchronousCallStatus::kTimeout,
154}
155
156template <typename Response>
157constexpr SynchronousCallResult<Response>
158SynchronousCallResult<Response>::RpcError(Status status) {
159 return SynchronousCallResult(internal::SynchronousCallStatus::kRpc, status);
160}
161
162template <typename Response>
163constexpr bool SynchronousCallResult<Response>::is_error() const {
164 return !ok();
165}
166
167template <typename Response>
168constexpr bool SynchronousCallResult<Response>::ok() const {
169 return is_server_response() && status_.ok();
170}
171
172template <typename Response>
173constexpr bool SynchronousCallResult<Response>::is_server_error() const {
174 return is_server_response() && !status_.ok();
175}
176
177template <typename Response>
178constexpr bool SynchronousCallResult<Response>::is_timeout() const {
179 return call_status_ == internal::SynchronousCallStatus::kTimeout;
180}
181
182template <typename Response>
183constexpr bool SynchronousCallResult<Response>::is_rpc_error() const {
184 return call_status_ == internal::SynchronousCallStatus::kRpc;
185}
186
187template <typename Response>
188constexpr bool SynchronousCallResult<Response>::is_server_response() const {
189 return call_status_ == internal::SynchronousCallStatus::kServer;
190}
191
192template <typename Response>
193constexpr Status SynchronousCallResult<Response>::status() const {
194 PW_ASSERT(call_status_ != internal::SynchronousCallStatus::kInvalid);
195 return status_;
196}
197
198template <typename Response>
199constexpr const Response& SynchronousCallResult<Response>::response() const& {
200 PW_ASSERT(is_server_response());
201 return response_;
202}
203
204template <typename Response>
205constexpr Response& SynchronousCallResult<Response>::response() & {
206 PW_ASSERT(is_server_response());
207 return response_;
208}
209
210template <typename Response>
211constexpr const Response&& SynchronousCallResult<Response>::response() const&& {
212 PW_ASSERT(is_server_response());
213 return std::move(response_);
214}
215
216template <typename Response>
217constexpr Response&& SynchronousCallResult<Response>::response() && {
218 PW_ASSERT(is_server_response());
219 return std::move(response_);
220}
221
222template <typename Response>
223constexpr const Response& SynchronousCallResult<Response>::operator*() const& {
224 PW_ASSERT(is_server_response());
225 return response_;
226}
227
228template <typename Response>
229constexpr Response& SynchronousCallResult<Response>::operator*() & {
230 PW_ASSERT(is_server_response());
231 return response_;
232}
233
234template <typename Response>
235constexpr const Response&& SynchronousCallResult<Response>::operator*()
236 const&& {
237 PW_ASSERT(is_server_response());
238 return std::move(response_);
239}
240
241template <typename Response>
242constexpr Response&& SynchronousCallResult<Response>::operator*() && {
243 PW_ASSERT(is_server_response());
244 return std::move(response_);
245}
246
247template <typename Response>
248constexpr const Response* SynchronousCallResult<Response>::operator->() const {
249 PW_ASSERT(is_server_response());
250 return &response_;
251}
252
253template <typename Response>
254constexpr Response* SynchronousCallResult<Response>::operator->() {
255 PW_ASSERT(is_server_response());
256 return &response_;
257}
258
259} // namespace rpc
260
261// Conversion functions for usage with PW_TRY and PW_TRY_ASSIGN.
262namespace internal {
263
264template <typename T>
265constexpr Status ConvertToStatus(const rpc::SynchronousCallResult<T>& result) {
266 return result.status();
267}
268
269template <typename T>
270constexpr T ConvertToValue(rpc::SynchronousCallResult<T>& result) {
271 return std::move(result.response());
272}
273
274} // namespace internal
275} // namespace pw
Definition: status.h:109
static constexpr Status DeadlineExceeded()
Deadline passed before operation completed.
Definition: status.h:136
Definition: synchronous_call_result.h:73
The Pigweed namespace.
Definition: alignment.h:27