blob: f79468b300b1199cc6302565bad733b204342efe [file] [log] [blame]
// Copyright 2022 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_UTILS_RESULT_RESULT_H_
#define SRC_TINT_UTILS_RESULT_RESULT_H_
#include <utility>
#include <variant>
#include "src/tint/utils/ice/ice.h"
#include "src/tint/utils/text/string_stream.h"
#include "src/tint/utils/traits/traits.h"
namespace tint {
/// Empty structure that can be used as the SUCCESS_TYPE for a Result.
struct SuccessType {};
/// An instance of SuccessType that can be used as a generic success value for a Result.
static constexpr const SuccessType Success;
/// Empty structure used as the default FAILURE_TYPE for a Result.
struct FailureType {};
/// An instance of FailureType which can be used as a generic failure value by Result
static constexpr const FailureType Failure;
/// Result is a helper for functions that need to return a value, or an failure value.
/// Result can be constructed with either a 'success' or 'failure' value.
/// @tparam SUCCESS_TYPE the 'success' value type.
/// @tparam FAILURE_TYPE the 'failure' value type. Defaults to FailureType which provides no
/// information about the failure, except that something failed. Must not be the same type
/// as SUCCESS_TYPE.
template <typename SUCCESS_TYPE, typename FAILURE_TYPE = FailureType>
struct [[nodiscard]] Result {
static_assert(!std::is_same_v<SUCCESS_TYPE, FAILURE_TYPE>,
"Result must not have the same type for SUCCESS_TYPE and FAILURE_TYPE");
/// Default constructor initializes to invalid state
Result() : value(std::monostate{}) {}
/// Constructor
/// @param success the success result
Result(const SUCCESS_TYPE& success) // NOLINT(runtime/explicit):
: value{success} {}
/// Constructor
/// @param success the success result
Result(SUCCESS_TYPE&& success) // NOLINT(runtime/explicit):
: value(std::move(SUCCESS_TYPE(std::move(success)))) {}
/// Constructor
/// @param failure the failure result
Result(const FAILURE_TYPE& failure) // NOLINT(runtime/explicit):
: value{failure} {}
/// Constructor
/// @param failure the failure result
Result(FAILURE_TYPE&& failure) // NOLINT(runtime/explicit):
: value{std::move(failure)} {}
/// Copy constructor with success / failure casting
/// @param other the Result to copy
template <typename S,
typename F,
typename = std::void_t<decltype(SUCCESS_TYPE{std::declval<S>()}),
decltype(FAILURE_TYPE{std::declval<F>()})>>
Result(const Result<S, F>& other) { // NOLINT(runtime/explicit):
if (other) {
value = SUCCESS_TYPE{other.Get()};
} else {
value = FAILURE_TYPE{other.Failure()};
}
}
/// @returns true if the result was a success
operator bool() const {
Validate();
return std::holds_alternative<SUCCESS_TYPE>(value);
}
/// @returns true if the result was a failure
bool operator!() const {
Validate();
return std::holds_alternative<FAILURE_TYPE>(value);
}
/// @returns the success value
/// @warning attempting to call this when the Result holds an failure will result in UB.
const SUCCESS_TYPE* operator->() const {
Validate();
return &(Get());
}
/// @returns the success value
/// @warning attempting to call this when the Result holds an failure value will result in UB.
const SUCCESS_TYPE& Get() const {
Validate();
return std::get<SUCCESS_TYPE>(value);
}
/// @returns the success value
/// @warning attempting to call this when the Result holds an failure value will result in UB.
SUCCESS_TYPE& Get() {
Validate();
return std::get<SUCCESS_TYPE>(value);
}
/// @returns the success value
/// @warning attempting to call this when the Result holds an failure value will result in UB.
SUCCESS_TYPE&& Move() {
Validate();
return std::get<SUCCESS_TYPE>(std::move(value));
}
/// @returns the failure value
/// @warning attempting to call this when the Result holds a success value will result in UB.
const FAILURE_TYPE& Failure() const {
Validate();
return std::get<FAILURE_TYPE>(value);
}
/// Equality operator
/// @param val the value to compare this Result to
/// @returns true if this result holds a success value equal to `value`
bool operator==(SUCCESS_TYPE val) const {
Validate();
if (auto* v = std::get_if<SUCCESS_TYPE>(&value)) {
return *v == val;
}
return false;
}
/// Equality operator
/// @param val the value to compare this Result to
/// @returns true if this result holds a failure value equal to `value`
bool operator==(FAILURE_TYPE val) const {
Validate();
if (auto* v = std::get_if<FAILURE_TYPE>(&value)) {
return *v == val;
}
return false;
}
private:
void Validate() const { TINT_ASSERT(!std::holds_alternative<std::monostate>(value)); }
/// The result. Either a success of failure value.
std::variant<std::monostate, SUCCESS_TYPE, FAILURE_TYPE> value;
};
/// Writes the result to the stream.
/// @param out the stream to write to
/// @param res the result
/// @return the stream so calls can be chained
template <typename STREAM,
typename SUCCESS,
typename FAILURE,
typename = traits::EnableIfIsOStream<STREAM>>
auto& operator<<(STREAM& out, Result<SUCCESS, FAILURE> res) {
return res ? (out << "success: " << res.Get()) : (out << "failure: " << res.Failure());
}
} // namespace tint
#endif // SRC_TINT_UTILS_RESULT_RESULT_H_