1 // Copyright 2023 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 
15 #pragma once
16 #include <cpp-string/string_printf.h>
17 #include <lib/fit/result.h>
18 
19 #include <type_traits>
20 #include <variant>
21 
22 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/host_error.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
26 
27 namespace bt {
28 
29 // Type used to hold either a HostError or a ProtocolErrorCode, a
30 // protocol-defined code. This can not be constructed in such a way to represent
31 // a success or to contain the product of a successful operation, but to be used
32 // as the error type parameter of a generic result type like
33 // fit::result<Error<…>> or fit::result<Error<…>, V>.
34 //
35 // Errors can be directly constructed from HostErrors. ProtocolErrorCodes whose
36 // possible values all represent errors can also be used to construct Errors.
37 // Otherwise, ProtocolErrorCodes like that used by HCI must be converted using
38 // ToResult in order to capture success values.
39 template <typename ProtocolErrorCode>
40 class Error;
41 
42 // Required trait for ProtocolErrorCode types.
43 template <typename ProtocolErrorCode>
44 struct ProtocolErrorTraits {
45   // Returns a string representation of the given ProtocolErrorCode value.
46   static std::string ToString(ProtocolErrorCode);
47 
48   // Optional: returns true if the given ProtocolErrorCode value represents
49   // success. If no such value exists, do not declare this static function in
50   // the specialization. static constexpr bool is_success(ProtocolErrorCode);
51 };
52 
53 // Marker used to indicate that an Error holds only HostError.
54 class NoProtocolError {
55   constexpr NoProtocolError() = delete;
56 };
57 
58 template <>
59 struct ProtocolErrorTraits<NoProtocolError> {
60   // This won't be called but still needs to be stubbed out to link correctly.
61   static std::string ToString(NoProtocolError) {
62     PW_CHECK(false);
63     return std::string();
64   }
65 };
66 
67 namespace detail {
68 
69 // Detects whether the given expression implicitly converts to a bt::Error.
70 template <typename T, typename = void>
71 struct IsError : std::false_type {};
72 
73 // This specialization is used when
74 //   1. T can be deduced as a template template (i.e. T<U>)
75 //   2. A function that takes Error<U> would accept a T<U>&& value as its
76 //   parameter
77 template <template <typename> class T, typename U>
78 struct IsError<T<U>,
79                std::void_t<decltype(std::declval<void (&)(Error<U>)>()(
80                    std::declval<T<U>>()))>> : std::true_type {};
81 
82 template <typename T>
83 constexpr bool IsErrorV = IsError<T>::value;
84 
85 // Detects whether ProtocolErrorTraits<ProtocolErrorCode>::is_success has been
86 // declared.
87 template <typename ProtocolErrorCode, typename = void>
88 struct CanRepresentSuccess : std::false_type {};
89 
90 template <typename ProtocolErrorCode>
91 struct CanRepresentSuccess<
92     ProtocolErrorCode,
93     std::void_t<decltype(ProtocolErrorTraits<ProtocolErrorCode>::is_success(
94         std::declval<ProtocolErrorCode>()))>> : std::true_type {};
95 
96 template <typename ProtocolErrorCode>
97 constexpr bool CanRepresentSuccessV =
98     CanRepresentSuccess<ProtocolErrorCode>::value;
99 
100 }  // namespace detail
101 
102 // Create a fit::result<Error<…>> from a HostError. The template parameter may
103 // be omitted to default to an fit::result<Error<NoProtocolError>> in the case
104 // that it's not useful to specify the kind of protocol error that the result
105 // could hold instead.
106 template <typename ProtocolErrorCode = NoProtocolError>
107 [[nodiscard]] constexpr fit::result<Error<ProtocolErrorCode>> ToResult(
108     HostError host_error) {
109   return fit::error(Error<ProtocolErrorCode>(host_error));
110 }
111 
112 // Create a fit::result<Error<…>> from a protocol error.
113 // This overload doesn't collide with the above when instantiated with
114 // <HostError>, because this would try to construct an invalid Error<HostError>.
115 template <typename ProtocolErrorCode>
116 [[nodiscard]] constexpr fit::result<Error<ProtocolErrorCode>> ToResult(
117     ProtocolErrorCode proto_error) {
118   if constexpr (detail::CanRepresentSuccessV<ProtocolErrorCode>) {
119     if (ProtocolErrorTraits<ProtocolErrorCode>::is_success(proto_error)) {
120       return fit::success();
121     }
122   }
123   return fit::error(Error(std::move(proto_error)));
124 }
125 
126 template <typename ProtocolErrorCode = NoProtocolError>
127 class [[nodiscard]] Error {
128   static_assert(!std::is_same_v<HostError, ProtocolErrorCode>,
129                 "HostError can not be a protocol error");
130   static_assert(!detail::IsErrorV<ProtocolErrorCode>,
131                 "ProtocolErrorCode can not be a bt::Error");
132 
133  public:
134   Error() = delete;
135   ~Error() = default;
136   constexpr Error(const Error&) = default;
137   constexpr Error(Error&&) noexcept = default;
138   constexpr Error& operator=(const Error&) = default;
139   constexpr Error& operator=(Error&&) noexcept = default;
140 
141   constexpr explicit Error(const HostError& host_error) : error_(host_error) {}
142 
143   // This is disabled if ProtocolErrorCode may hold a value that means success,
144   // leaving only the private ctor. Instead use ToResult(ProtocolErrorCode),
145   // whose return value may hold success.
146   template <typename T = ProtocolErrorCode,
147             std::enable_if_t<!detail::CanRepresentSuccessV<T>, int> = 0>
148   constexpr explicit Error(const ProtocolErrorCode& proto_error)
149       : error_(proto_error) {}
150 
151   // Intentionally implicit conversion from Error<NoProtocolError> that holds
152   // only HostErrors. This allows any Error<…> to be compared to an
153   // Error<NoProtocolError>'s HostError payload. Also, functions that accept
154   // Error<…> will take Error<NoProtocolError> without an explicit conversion.
155   //
156   // Example:
157   //   void Foo(Error<BarErrorCode>);
158   //   Foo(ToResult(HostError::kTimedOut));  // Compiles without having to write
159   //   BarErrorCode
160   //
161   // For safety, this implicit conversion does not "chain" to allow bare
162   // ProtocolErrorCodes or HostErrors to be converted into Error or fit::result.
163   //
164   // The seemingly-extraneous template parameter serves to disable this overload
165   // when |*this| is an Error<NoProtocolError>
166   template <typename T = ProtocolErrorCode,
167             std::enable_if_t<Error<T>::may_hold_protocol_error(), int> = 0>
168   // NOLINTNEXTLINE(google-explicit-constructor)
169   constexpr Error(const Error<NoProtocolError>& other)
170       : error_(other.host_error()) {}
171 
172   // Evaluates to true if and only if both Errors hold the same class of error
173   // (host vs protocol) and their codes match in value. Errors with different
174   // ProtocolErrorCodes are comparable only if their ProtocolErrorCodes have a
175   // defined operator==, which is generally not the case if the codes are
176   // strongly-type "enum class" enumerations.
177   template <typename RErrorCode>
178   constexpr bool operator==(const Error<RErrorCode>& rhs) const {
179     auto proto_error_visitor = [&](ProtocolErrorCode held) {
180       if constexpr (may_hold_protocol_error() &&
181                     Error<RErrorCode>::may_hold_protocol_error()) {
182         return held == rhs.protocol_error();
183       } else {
184         // This unreachable branch makes comparisons to Error<NoProtocolError>
185         // well-defined, so that the lambda compiles as long as the protocol
186         // error codes are comparable.
187         return false;
188       }
189     };
190     if (is_host_error() != rhs.is_host_error()) {
191       return false;
192     }
193     return Visit([&rhs](HostError held) { return held == rhs.host_error(); },
194                  proto_error_visitor);
195   }
196 
197   template <typename RErrorCode>
198   constexpr bool operator!=(const Error<RErrorCode>& rhs) const {
199     return !(*this == rhs);
200   }
201 
202   [[nodiscard]] std::string ToString() const {
203     return Visit(
204         [](HostError held) { return HostErrorToString(held); },
205         [](ProtocolErrorCode held) {
206           return ProtocolErrorTraits<ProtocolErrorCode>::ToString(held);
207         });
208   }
209 
210   [[nodiscard]] constexpr bool is_host_error() const {
211     return std::holds_alternative<HostError>(error_);
212   }
213 
214   [[nodiscard]] constexpr bool is_protocol_error() const {
215     return std::holds_alternative<ProtocolErrorCode>(error_);
216   }
217 
218   [[nodiscard]] constexpr HostError host_error() const {
219     PW_ASSERT(is_host_error());
220     return std::get<HostError>(error_);
221   }
222 
223   [[nodiscard]] constexpr ProtocolErrorCode protocol_error() const {
224     PW_ASSERT(is_protocol_error());
225     return std::get<ProtocolErrorCode>(error_);
226   }
227 
228   [[nodiscard]] constexpr bool is(ProtocolErrorCode proto_error) const {
229     return Visit(
230         [](HostError) { return false; },
231         [proto_error](ProtocolErrorCode held) { return held == proto_error; });
232   }
233 
234   [[nodiscard]] constexpr bool is(HostError host_error) const {
235     return Visit([host_error](HostError held) { return held == host_error; },
236                  [](ProtocolErrorCode) { return false; });
237   }
238 
239   template <typename... Ts>
240   [[nodiscard]] constexpr bool is_any_of(Ts... error_codes) const {
241     return (is(error_codes) || ...);
242   }
243 
244   // Given two "visitors" (callable objects that accept HostError and
245   // ProtocolErrorCode), invoke the one that corresponds to the error held in
246   // storage, but not the other. This pattern allows the code within the
247   // visitors to statically presume the type of the error code that they work
248   // with. Example:
249   //
250   //   int ConvertToInt(Error<FooError> error) {
251   //     return Visit(
252   //         [](HostError held) { return static_cast<int>(held); },
253   //         [](ProtocolErrorCode held) { return static_cast<int>(held); });
254   //     );
255   //   }
256   //
257   // Unlike std::visit, the two visitors do not need to be differentiated from
258   // each other through overload resolution rules: the argument order to
259   // invoking Visit(…) is what determines which visitor gets called.
260   //
261   // Returns the return value of the visitor that was called (which may return
262   // void).
263   template <typename HostVisitor, typename ProtoVisitor>
264   [[nodiscard]] constexpr std::common_type_t<
265       std::invoke_result_t<HostVisitor, HostError>,
266       std::invoke_result_t<ProtoVisitor, ProtocolErrorCode>>
267   Visit(HostVisitor host_error_visitor,
268         ProtoVisitor proto_error_visitor) const {
269     if (is_host_error()) {
270       return host_error_visitor(host_error());
271     }
272     return proto_error_visitor(protocol_error());
273   }
274 
275   static constexpr bool may_hold_protocol_error() {
276     return !std::is_same_v<ProtocolErrorCode, NoProtocolError>;
277   }
278 
279  private:
280   // Factory functions
281   friend constexpr fit::result<Error<ProtocolErrorCode>>
282       ToResult<ProtocolErrorCode>(ProtocolErrorCode);
283 
284   template <typename T = ProtocolErrorCode,
285             std::enable_if_t<detail::CanRepresentSuccessV<T>, int> = 0>
286   constexpr explicit Error(const ProtocolErrorCode& proto_error)
287       : error_(proto_error) {
288     PW_ASSERT(!ProtocolErrorTraits<ProtocolErrorCode>::is_success(proto_error));
289   }
290 
291   std::variant<HostError, ProtocolErrorCode> error_;
292 };
293 
294 // Deduction guide to allow Errors to be constructed from a HostError without
295 // specifying what protocol error the Error can hold instead.
296 Error(HostError) -> Error<NoProtocolError>;
297 
298 // Comparison operators overloads useful for testing using
299 // {ASSERT,EXPECT}_{EQ,NE} GoogleTest macros. Each of these must explicitly
300 // define a operator!= as well as account for commutative calls, because C++
301 // does not automatically generate these. Those variant overloads can not be
302 // generically defined because there's no way to test if those variants can
303 // actually be instantiated (using decltype etc), causing problems with e.g.
304 // fit::result<E, T> == fit::result<F, U>.
305 
306 // Comparisons to fit::result<Error<ProtocolErrorCode>>
307 template <typename LErrorCode, typename RErrorCode, typename... Ts>
308 constexpr bool operator==(const Error<LErrorCode>& lhs,
309                           const fit::result<Error<RErrorCode>, Ts...>& rhs) {
310   static_assert((!detail::IsErrorV<Ts> && ...),
311                 "fit::result should not contain Error as a success value");
312   return rhs.is_error() && (rhs.error_value() == lhs);
313 }
314 
315 template <typename LErrorCode, typename RErrorCode, typename... Ts>
316 constexpr bool operator==(const fit::result<Error<LErrorCode>, Ts...>& lhs,
317                           const Error<RErrorCode>& rhs) {
318   return rhs == lhs;
319 }
320 
321 template <typename LErrorCode, typename RErrorCode, typename... Ts>
322 constexpr bool operator!=(const Error<LErrorCode>& lhs,
323                           const fit::result<Error<RErrorCode>, Ts...>& rhs) {
324   return !(lhs == rhs);
325 }
326 
327 template <typename LErrorCode, typename RErrorCode, typename... Ts>
328 constexpr bool operator!=(const fit::result<Error<LErrorCode>, Ts...>& lhs,
329                           const Error<RErrorCode>& rhs) {
330   return !(rhs == lhs);
331 }
332 
333 // Comparisons between fit::result<Error<…>> objects
334 // Note that this is not standard fit::result relation behavior which normally
335 // compares all error results to be equal. These are preferred in overload
336 // resolution because they are more specialized templates than the ones provided
337 // by fit. However, because they are more specialized, all of the combinations
338 // must be typed out separately to avoid ambiguous overload errors:
339 //   1. operands having zero or one success values
340 //   2. operation is == or !=
341 // The case of comparing a result with a success value to a result without is
342 // intentionally not defined because it's not obvious what behavior it should
343 // have when both results hold success.
344 template <typename LErrorCode, typename RErrorCode, typename T>
345 constexpr bool operator==(const fit::result<Error<LErrorCode>, T>& lhs,
346                           const fit::result<Error<RErrorCode>, T>& rhs) {
347   static_assert(!detail::IsErrorV<T>,
348                 "fit::result should not contain Error as a success value");
349   if (lhs.is_ok() != rhs.is_ok()) {
350     return false;
351   }
352   if (lhs.is_ok()) {
353     return lhs.value() == rhs.value();
354   }
355   return lhs.error_value() == rhs.error_value();
356 }
357 
358 template <typename LErrorCode, typename RErrorCode, typename T>
359 constexpr bool operator!=(const fit::result<Error<LErrorCode>, T>& lhs,
360                           const fit::result<Error<RErrorCode>, T>& rhs) {
361   return !(lhs == rhs);
362 }
363 
364 template <typename LErrorCode, typename RErrorCode>
365 constexpr bool operator==(const fit::result<Error<LErrorCode>>& lhs,
366                           const fit::result<Error<RErrorCode>>& rhs) {
367   if (lhs.is_ok() != rhs.is_ok()) {
368     return false;
369   }
370   if (lhs.is_ok()) {
371     return true;
372   }
373   return lhs.error_value() == rhs.error_value();
374 }
375 
376 template <typename LErrorCode, typename RErrorCode>
377 constexpr bool operator!=(const fit::result<Error<LErrorCode>>& lhs,
378                           const fit::result<Error<RErrorCode>>& rhs) {
379   return !(lhs == rhs);
380 }
381 
382 namespace internal {
383 
384 // Helper to build a string from result using generic concatenation calls.
385 template <typename Result, typename StringBuilder, typename ValueStringBuilder>
386 void BuildResultToString(const Result& result,
387                          StringBuilder builder,
388                          ValueStringBuilder append_value) {
389   builder("[result: ");
390   if (result.is_ok()) {
391     builder("ok(");
392     append_value();
393   } else {
394     builder("error(");
395     builder(result.error_value().ToString());
396   }
397   builder(")]");
398 }
399 
400 // Produces a human-readable representation of a fit::result<Error<…>>
401 template <typename ProtocolErrorCode, typename... Ts>
402 std::string ToString(
403     const fit::result<Error<ProtocolErrorCode>, Ts...>& result) {
404   std::string out;
405   auto append_value_string = [&] {
406     if constexpr (sizeof...(Ts) > 0) {
407       if constexpr ((bt::internal::HasToStringV<Ts> && ...)) {
408         out += ToString(result.value());
409       } else {
410         // It's not possible to portably print e.g. the name of the value's
411         // type, so fall back to a placeholder. It may be useful to print the
412         // size and a hexdump, however.
413         out += "?";
414       }
415     }
416   };
417   bt::internal::BuildResultToString(
418       result, [&](auto s) { out += s; }, append_value_string);
419   return out;
420 }
421 
422 }  // namespace internal
423 
424 namespace detail {
425 
426 // Contains a |value| bool member that is true if overload operator<<(Lhs&,
427 // const Rhs&) exists
428 template <typename Lhs, typename Rhs, typename = void>
429 struct IsStreamable : std::false_type {};
430 
431 template <typename Lhs, typename Rhs>
432 struct IsStreamable<
433     Lhs,
434     Rhs,
435     std::void_t<decltype(std::declval<Lhs&>() << std::declval<const Rhs&>())>>
436     : std::is_same<Lhs&,
437                    decltype(std::declval<Lhs&>()
438                             << std::declval<const Rhs&>())> {};
439 
440 template <typename Lhs, typename Rhs>
441 constexpr bool IsStreamableV = IsStreamable<Lhs, Rhs>::value;
442 
443 }  // namespace detail
444 }  // namespace bt
445 
446 // Extends the GoogleTest value printer to print fit::result<bt::Error<…>, …>
447 // types, including converting the contained value of "success" results to
448 // strings when possible.
449 //
450 // This must be defined in namespace fit because GoogleTest uses
451 // argument-dependent lookup (ADL) to find this overload. |os|'s type is
452 // templated in order to avoid including <iostream>.
453 namespace fit {
454 
455 // Some GoogleTest internal objects (like testing::Message, the return type of
456 // ADD_FAILURE()) declare broad operator<< overloads that conflict with this
457 // one. In those cases, it's likely easiest to wrap the result in bt_str(…).
458 template <typename OStream, typename ProtocolErrorCode, typename... Ts>
459 OStream& operator<<(
460     OStream& os,
461     const fit::result<::bt::Error<ProtocolErrorCode>, Ts...>& result) {
462   auto stream_value_string = [&] {
463     if constexpr (sizeof...(Ts) > 0) {
464       if constexpr ((::bt::internal::HasToStringV<Ts> && ...)) {
465         os << ::bt::internal::ToString(result.value());
466       } else if constexpr ((::bt::detail::IsStreamableV<OStream, Ts> && ...)) {
467         os << result.value();
468       } else {
469         // It may be prettier to default to ::testing::PrintToString here but
470         // that would require including <gtest/gtest.h> here, which is not
471         // ideal.
472         os << "?";
473       }
474     }
475   };
476   ::bt::internal::BuildResultToString(
477       result, [&](auto s) { os << s; }, stream_value_string);
478   return os;
479 }
480 
481 }  // namespace fit
482 
483 // Macro to check and log any non-Success status of an event.
484 // Use these like:
485 // if (bt_is_error(result, WARN, "gap", "failed to set event mask")) {
486 //   ...
487 //   return;
488 // }
489 //
490 // It will log with the string prepended to the stringified result if result is
491 // a failure. Evaluates to true if the result indicates failure.
492 #define bt_is_error(result, level, tag, fmt, /*args*/...)             \
493   [&]() {                                                             \
494     auto _result = result;                                            \
495     if (_result.is_error())                                           \
496       bt_log(level, tag, "%s: " fmt, bt_str(_result), ##__VA_ARGS__); \
497     return _result.is_error();                                        \
498   }()
499