blob: 2c6bd8c22fb6b84866ace61df532baaac43e2b00 [file] [log] [blame]
// Copyright (c) 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef QUICHE_COMMON_QUICHE_STATUS_UTILS_H_
#define QUICHE_COMMON_QUICHE_STATUS_UTILS_H_
#include <utility>
#include "absl/base/optimization.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h" // IWYU pragma: keep
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
namespace quiche {
// A simplified version of the standard google3 "return if error" macro. Unlike
// the standard version, this does not come with a StatusBuilder support; the
// AppendToStatus() function below is meant to partially fill that gap.
#define QUICHE_RETURN_IF_ERROR(expr) \
do { \
absl::Status quiche_status_macro_value = (expr); \
if (ABSL_PREDICT_FALSE(!quiche_status_macro_value.ok())) { \
return quiche_status_macro_value; \
} \
} while (0)
// A simplified version of the standard google3 "assign or return" macro.
// Unlike the standard version, this does not come with a StatusBuilder support;
// the optional error-mapper lambda parameter is meant to partially fill that
// gap.
//
// Example usage, where `Calculate()` returns `absl::StatusOr<Foo>`:
//
// QUICHE_ASSIGN_OR_RETURN(Foo x, Calculate());
//
// Optionally, the macro also accepts an error-mapper lambda that takes a `const
// absl::Status&` and returns `absl::Status`. The lambda is only invoked when
// the status is an error. When it is called, its return value becomes the
// caller's return value.
//
// Example of using the error-mapper lambda to transform the error:
//
// QUICHE_ASSIGN_OR_RETURN(Foo x, Calculate(),
// [](const absl::Status& status) {
// return absl::InvalidArgumentError(absl::StrCat(
// "custom message: ", status.message()));
// });
//
// Example of using the error-mapper lambda purely for its side effects:
//
// QUICHE_ASSIGN_OR_RETURN(Foo x, Calculate(),
// [](const absl::Status& status) {
// QUICHE_LOG(INFO) << status;
// return status;
// });
#define QUICHE_ASSIGN_OR_RETURN(lhs, expr, ...) \
QUICHE_ASSIGN_OR_RETURN_IMPL( \
lhs, (expr), \
QUICHE_STATUS_UTILS_INTERNAL_CONCAT2( \
quiche_status_utils_internal_statusor, __COUNTER__), \
QUICHE_STATUS_UTILS_INTERNAL_CONCAT2( \
quiche_status_utils_internal_status, __COUNTER__), \
QUICHE_STATUS_UTILS_INTERNAL_CONCAT2( \
quiche_status_utils_internal_lambda, __COUNTER__) __VA_OPT__(, ) \
__VA_ARGS__)
// Copies absl::Status payloads from `original` to `target`; required to copy a
// status correctly.
inline void CopyStatusPayloads(const absl::Status& original,
absl::Status& target) {
original.ForEachPayload([&](absl::string_view key, const absl::Cord& value) {
target.SetPayload(key, value);
});
}
// Appends additional into to a status message if the status message is
// an error.
template <typename... T>
absl::Status AppendToStatus(absl::Status input, T&&... args) {
if (ABSL_PREDICT_TRUE(input.ok())) {
return input;
}
absl::Status result = absl::Status(
input.code(), absl::StrCat(input.message(), std::forward<T>(args)...));
CopyStatusPayloads(input, result);
return result;
}
// ========================================================================
// == Implementation details. Do not depend on anything below this line. ==
// ========================================================================
#ifndef __COUNTER__
static_assert(false, "QUICHE_ASSIGN_OR_RETURN requires the __COUNTER__ macro.");
#endif
// clang-format off
#define QUICHE_ASSIGN_OR_RETURN_IMPL(lhs, expr, statusor_ident, status_ident, \
lambda_ident, ...) \
auto statusor_ident = (expr); \
if (ABSL_PREDICT_FALSE(!statusor_ident.ok())) { \
absl::Status status_ident = statusor_ident.status(); \
/* Invoke the error-mapper lambda if the variadic args are non-empty. */ \
__VA_OPT__( \
auto lambda_ident = (__VA_ARGS__); \
static_assert(std::is_convertible_v< \
decltype(lambda_ident), \
std::function<absl::Status(const absl::Status&)>>, \
"QUICHE_ASSIGN_OR_RETURN: the on-error lambda has an " \
"unexpected type"); \
status_ident = lambda_ident(status_ident); \
) \
return status_ident; \
} \
lhs = std::move(statusor_ident).value();
// clang-format on
#define QUICHE_STATUS_UTILS_INTERNAL_CONCAT2(a, b) \
QUICHE_STATUS_UTILS_INTERNAL_CONCAT2_IMPL(a, b)
// Without this multi-layer concat macro, `__COUNTER__` would not be expanded.
#define QUICHE_STATUS_UTILS_INTERNAL_CONCAT2_IMPL(a, b) a##b
} // namespace quiche
#endif // QUICHE_COMMON_QUICHE_STATUS_UTILS_H_