Skip to content

Commit

Permalink
Refactor Result class to support empty values and same value and erro…
Browse files Browse the repository at this point in the history
…r types
  • Loading branch information
GregoryKogan committed Dec 21, 2023
1 parent 615019e commit 6c62d55
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 9 deletions.
46 changes: 37 additions & 9 deletions src/Result/Result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,54 @@
namespace res {

template <typename T, typename E> class Result {
std::variant<T, E> content;
static_assert(!std::is_same_v<E, std::monostate>, "E must not be std::monostate");

explicit Result(const T &value) : content(value) {}
explicit Result(const E &value) : content(value) {}
enum ContentType { Value, Error };
std::variant<T, E> content_;

auto emplace_value(const T &value) -> void { content_.template emplace<ContentType::Value>(value); }
auto emplace_error(const E &error) -> void { content_.template emplace<ContentType::Error>(error); }

public:
static auto Ok(const T &value) -> Result { return Result<T, E>(value); }
static auto Err(const E &value) -> Result { return Result<T, E>(value); }
[[nodiscard]] static auto Ok(const T &value) -> Result<T, E> {
Result<T, E> result;
result.emplace_value(value);
return result;
}
[[nodiscard]] static auto Ok(T &&value) -> Result<T, E> {
Result<T, E> result;
result.emplace_value(std::move(value));
return result;
}
[[nodiscard]] static auto Ok() -> Result<std::monostate, E> {
Result<std::monostate, E> result;
result.emplace_value(std::monostate{});
return result;
}

[[nodiscard]] static auto Err(const E &error) -> Result<T, E> {
Result<T, E> result;
result.emplace_error(error);
return result;
}
[[nodiscard]] static auto Err(E &&error) -> Result<T, E> {
Result<T, E> result;
result.emplace_error(std::move(error));
return result;
}

[[nodiscard]] auto is_ok() const -> bool { return std::holds_alternative<T>(content); }
[[nodiscard]] auto is_err() const -> bool { return std::holds_alternative<E>(content); }
[[nodiscard]] auto is_ok() const -> bool { return content_.index() == ContentType::Value; }
[[nodiscard]] auto is_err() const -> bool { return content_.index() == ContentType::Error; }

[[nodiscard]] auto unwrap() const -> T {
if (is_err()) { throw std::runtime_error("Called unwrap on an Err value"); }
return std::get<T>(content);
if constexpr (std::is_same_v<T, std::monostate>) { throw std::runtime_error("Called unwrap on an empty value"); }
return std::get<ContentType::Value>(content_);
}

[[nodiscard]] auto unwrap_err() const -> E {
if (is_ok()) { throw std::runtime_error("Called unwrap_err on an Ok value"); }
return std::get<E>(content);
return std::get<ContentType::Error>(content_);
}
};

Expand Down
52 changes: 52 additions & 0 deletions tests/type_edgecases.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "../src/Result/Result.hpp"
#include <gtest/gtest.h>
#include <string>

TEST(Types, SameInt) {
const int value = 42;
const int error = 43;
auto ok_result = res::Result<int, int>::Ok(value);
auto err_result = res::Result<int, int>::Err(error);

EXPECT_TRUE(ok_result.is_ok());
EXPECT_FALSE(ok_result.is_err());
EXPECT_EQ(ok_result.unwrap(), value);
EXPECT_THROW(auto ___ = ok_result.unwrap_err(), std::runtime_error);

EXPECT_TRUE(err_result.is_err());
EXPECT_FALSE(err_result.is_ok());
EXPECT_EQ(err_result.unwrap_err(), error);
EXPECT_THROW(auto ___ = err_result.unwrap(), std::runtime_error);
}

TEST(Types, SameString) {
const std::string value = "Hello";
const std::string error = "World";
auto ok_result = res::Result<std::string, std::string>::Ok(value);
auto err_result = res::Result<std::string, std::string>::Err(error);

EXPECT_TRUE(ok_result.is_ok());
EXPECT_FALSE(ok_result.is_err());
EXPECT_EQ(ok_result.unwrap(), value);
EXPECT_THROW(auto ___ = ok_result.unwrap_err(), std::runtime_error);

EXPECT_TRUE(err_result.is_err());
EXPECT_FALSE(err_result.is_ok());
EXPECT_EQ(err_result.unwrap_err(), error);
EXPECT_THROW(auto ___ = err_result.unwrap(), std::runtime_error);
}

TEST(Types, EmptyValue) {
auto empty_ok_result = res::Result<std::monostate, std::string>::Ok();
auto empty_err_result = res::Result<std::monostate, std::string>::Err("Hello");

EXPECT_TRUE(empty_ok_result.is_ok());
EXPECT_FALSE(empty_ok_result.is_err());
EXPECT_THROW(auto ___ = empty_ok_result.unwrap(), std::runtime_error);
EXPECT_THROW(auto ___ = empty_ok_result.unwrap_err(), std::runtime_error);

EXPECT_TRUE(empty_err_result.is_err());
EXPECT_FALSE(empty_err_result.is_ok());
EXPECT_EQ(empty_err_result.unwrap_err(), "Hello");
EXPECT_THROW(auto ___ = empty_err_result.unwrap(), std::runtime_error);
}

0 comments on commit 6c62d55

Please sign in to comment.