diff --git a/Package.swift b/Package.swift index 8d06fd0d..4cae5eb9 100644 --- a/Package.swift +++ b/Package.swift @@ -549,6 +549,7 @@ let package = Package( "internal/platform/implementation/wifi_utils_test.cc", "internal/platform/atomic_boolean_test.cc", "internal/platform/exception_test.cc", + "internal/platform/expected_test.cc", "internal/platform/error_code_recorder_test.cc", "internal/platform/future_test.cc", "internal/platform/cancellation_flag_test.cc", diff --git a/internal/platform/BUILD b/internal/platform/BUILD index f47c0de5..b676cbaf 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -45,6 +45,7 @@ cc_library( "byte_array.h", "callable.h", "exception.h", + "expected.h", "feature_flags.h", "input_stream.h", "listeners.h", @@ -426,6 +427,17 @@ cc_test( ], ) +cc_test( + name = "expected_test", + srcs = ["expected_test.cc"], + deps = [ + ":base", + "//proto:connections_enums_cc_proto", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "error_code_recorder_test", srcs = [ diff --git a/internal/platform/expected.h b/internal/platform/expected.h new file mode 100644 index 00000000..04372246 --- /dev/null +++ b/internal/platform/expected.h @@ -0,0 +1,119 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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 PLATFORM_BASE_EXPECTED_H_ +#define PLATFORM_BASE_EXPECTED_H_ + +#include +#include +#include +#include + +#include "proto/connections_enums.pb.h" + +namespace nearby { + +class Error { + public: + Error() = default; + + explicit Error(location::nearby::proto::connections::OperationResultCode + operation_result_code) + : operation_result_code_(operation_result_code) {} + + bool success() const { + return operation_result_code_ == location::nearby::proto::connections:: + OperationResultCode::DETAIL_SUCCESS; + } + bool failure() const { return !success(); } + + std::optional + operation_result_code() const { + return operation_result_code_; + } + + static Error Success() { return Error(); } + + private: + location::nearby::proto::connections::OperationResultCode + operation_result_code_ = location::nearby::proto::connections:: + OperationResultCode::DETAIL_UNKNOWN; +}; + +// Forward declare. +template +class Unexpected; + +template +class Expected { + public: + constexpr Expected(T value) : data_(std::move(value)) {} // NOLINT + constexpr Expected(Unexpected u); // NOLINT + + constexpr operator bool() const { // NOLINT + return has_value(); + } + + constexpr T& operator*() & { return value(); } + constexpr const T& operator*() const& { return value(); } + constexpr T&& operator*() && { return std::move(value()); } + constexpr const T& operator*() const&& { return std::move(value()); } + + constexpr T* operator->() { return &value(); } + constexpr const T* operator->() const { return &value(); } + + constexpr bool has_value() const { return std::holds_alternative(data_); } + constexpr bool has_error() const { return std::holds_alternative(data_); } + + constexpr T& value() & { return std::get(data_); } + constexpr const T& value() const& { return std::get(data_); } + constexpr T&& value() && { return std::get(std::move(data_)); } + constexpr const T& value() const&& { return std::get(std::move(data_)); } + + constexpr E& error() & { return std::get(data_); } + constexpr const E& error() const& { return std::get(data_); } + constexpr E&& error() && { return std::get(std::move(data_)); } + constexpr const E&& error() const&& { return std::get(std::move(data_)); } + + private: + std::variant data_; +}; + +template +class Unexpected { + public: + constexpr Unexpected(E error) : error_(std::move(error)) {} // NOLINT + + private: + template + friend class Expected; + + E error_; +}; + +Unexpected(const char*) -> Unexpected; + +template +constexpr Expected::Expected(Unexpected u) + : data_(std::move(u.error_)) {} + +template +class ErrorOr : public Expected { + public: + using Expected::Expected; +}; + +} // namespace nearby + +#endif // PLATFORM_BASE_EXPECTED_H_ diff --git a/internal/platform/expected_test.cc b/internal/platform/expected_test.cc new file mode 100644 index 00000000..24743f6b --- /dev/null +++ b/internal/platform/expected_test.cc @@ -0,0 +1,51 @@ +// Copyright 2024 Google LLC +// +// 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 +// +// https://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. + +#include "internal/platform/expected.h" + +#include + +#include "gtest/gtest.h" +#include "proto/connections_enums.proto.h" + +namespace nearby { +using ::location::nearby::proto::connections::OperationResultCode; + +TEST(ExpectedTest, Expected) { + ErrorOr value = 42; + EXPECT_TRUE(value.has_value()); + EXPECT_FALSE(value.has_error()); + EXPECT_EQ(*value, 42); + + ErrorOr error{Error(OperationResultCode::DETAIL_UNKNOWN)}; + EXPECT_FALSE(error.has_value()); + EXPECT_TRUE(error.has_error()); + EXPECT_EQ(error.error().operation_result_code(), + OperationResultCode::DETAIL_UNKNOWN); + + ErrorOr> value2{std::make_unique(42)}; + EXPECT_TRUE(value2.has_value()); + EXPECT_FALSE(value2.has_error()); + EXPECT_NE(value2.value().get(), nullptr); + EXPECT_EQ(*value2.value(), 42); + + ErrorOr> error2{ + Error(OperationResultCode::IO_FILE_OPENING_ERROR)}; + EXPECT_FALSE(error2.has_value()); + EXPECT_TRUE(error2.has_error()); + EXPECT_EQ(error2.error().operation_result_code(), + OperationResultCode::IO_FILE_OPENING_ERROR); +} + +} // namespace nearby