blob: ec2e4b9978e40a4192a395d9d849b8c362b702f4 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 SRC_DEVELOPER_DEBUG_ZXDB_COMMON_ERR_OR_H_
#define SRC_DEVELOPER_DEBUG_ZXDB_COMMON_ERR_OR_H_
#include <lib/syslog/cpp/macros.h>
#include <variant>
#include "lib/fit/function.h"
#include "src/developer/debug/zxdb/common/err.h"
namespace zxdb {
template <typename T>
class ErrOr {
using Variant = std::variant<Err, T>;
public:
// The Err must be set when constructing this object in an error state.
ErrOr(Err e) : variant_(std::move(e)) { FX_DCHECK(err().has_error()); }
// Constructs with a value.
ErrOr(T v) : variant_(std::move(v)) {}
bool ok() const { return !std::holds_alternative<Err>(variant_); }
bool has_error() const { return std::holds_alternative<Err>(variant_); }
// Requires that has_error be true or this function will crash. See also err_or_empty().
const Err& err() const {
FX_DCHECK(has_error());
FX_DCHECK(std::get<Err>(variant_).has_error()); // Err should be set if present.
return std::get<Err>(variant_);
}
// Requires that has_error be false or this function will crash. See also [take_]value_or_empty().
const T& value() const {
FX_DCHECK(!has_error());
return std::get<T>(variant_);
}
T& value() {
FX_DCHECK(!has_error());
return std::get<T>(variant_);
}
T take_value() { // Destructively moves the value out.
FX_DCHECK(!has_error());
return std::move(std::get<T>(variant_));
}
// Implicit converstion to bool indicating "OK".
explicit operator bool() const { return ok(); }
// These functions are designed for integrating with code that uses an (Err, T) pair. If this
// class isn't in the corresponding state, default constructs the missing object.
//
// The value version can optionally destructively move the value out (with the "take" variant)
// since some values are inefficient to copy and often this is used in a context where the value
// is no longer needed.
//
// The Err version does not allow destructive moving because it would leave this object in an
// inconsistent state where the error object is stored in the variant, but err().has_error() is
// not set. We assume that errors are unusual so are not worth optimizing for saving one string
// copy to avoid this.
Err err_or_empty() const { // Makes a copy of the error.
if (has_error())
return std::get<Err>(variant_);
return Err();
}
T value_or_empty() const { // Makes a copy of the value.
if (!has_error())
return std::get<T>(variant_);
return T();
}
T take_value_or_empty() { // Destructively moves the value out.
if (!has_error())
return std::move(std::get<T>(variant_));
return T();
}
// Adapts an old-style callback that takes two parameters and returns a newer one that takes an
// ErrOr.
static fit::callback<void(ErrOr<T>)> FromPairCallback(fit::callback<void(const Err&, T)> cb) {
return [cb = std::move(cb)](ErrOr<T> value) mutable {
cb(value.err_or_empty(), value.take_value_or_empty());
};
}
private:
Variant variant_;
};
} // namespace zxdb
#endif // SRC_DEVELOPER_DEBUG_ZXDB_COMMON_ERR_OR_H_