// Jackson Coxson #pragma once #include #include #include #include namespace IdeviceFFI { namespace types { template struct Ok { T val; Ok(const T& val) : val(val) {} Ok(T&& val) : val(std::move(val)) {} }; template <> struct Ok {}; template struct Err { E val; Err(const E& val) : val(val) {} Err(E&& val) : val(std::move(val)) {} }; } // namespace types template inline types::Ok::type> Ok(T&& val) { return types::Ok::type>(std::forward(val)); } inline types::Ok Ok() { return types::Ok(); } template inline types::Err::type> Err(E&& val) { return types::Err::type>(std::forward(val)); } // ======================= // Result // ======================= template class Result { bool is_ok_; union { T ok_value_; E err_value_; }; public: Result(types::Ok ok_val) : is_ok_(true), ok_value_(std::move(ok_val.val)) {} Result(types::Err err_val) : is_ok_(false), err_value_(std::move(err_val.val)) {} Result(const Result& other) : is_ok_(other.is_ok_) { if (is_ok_) { new (&ok_value_) T(other.ok_value_); } else { new (&err_value_) E(other.err_value_); } } Result(Result&& other) noexcept : is_ok_(other.is_ok_) { if (is_ok_) { new (&ok_value_) T(std::move(other.ok_value_)); } else { new (&err_value_) E(std::move(other.err_value_)); } } ~Result() { if (is_ok_) { ok_value_.~T(); } else { err_value_.~E(); } } bool is_ok() const { return is_ok_; } bool is_err() const { return !is_ok_; } // lvalue (mutable) T& unwrap() & { if (!is_ok_) { std::fprintf(stderr, "unwrap on Err\n"); std::terminate(); } return ok_value_; } // lvalue (const) const T& unwrap() const& { if (!is_ok_) { std::fprintf(stderr, "unwrap on Err\n"); std::terminate(); } return ok_value_; } // rvalue (consume/move) T unwrap() && { if (!is_ok_) { std::fprintf(stderr, "unwrap on Err\n"); std::terminate(); } return std::move(ok_value_); } E& unwrap_err() & { if (is_ok_) { std::fprintf(stderr, "unwrap_err on Ok\n"); std::terminate(); } return err_value_; } const E& unwrap_err() const& { if (is_ok_) { std::fprintf(stderr, "unwrap_err on Ok\n"); std::terminate(); } return err_value_; } E unwrap_err() && { if (is_ok_) { std::fprintf(stderr, "unwrap_err on Ok\n"); std::terminate(); } return std::move(err_value_); } T unwrap_or(T&& default_value) const { return is_ok_ ? ok_value_ : std::move(default_value); } template T unwrap_or_else(F&& f) & { return is_ok_ ? ok_value_ : static_cast(f(err_value_)); } // const lvalue: returns T by copy template T unwrap_or_else(F&& f) const& { return is_ok_ ? ok_value_ : static_cast(f(err_value_)); } // rvalue: moves Ok(T) out; on Err(E), allow the handler to consume/move E template T unwrap_or_else(F&& f) && { if (is_ok_) { return std::move(ok_value_); } return static_cast(std::forward(f)(std::move(err_value_))); } }; // Result specialization template class Result { bool is_ok_; union { char dummy_; E err_value_; }; public: Result(types::Ok) : is_ok_(true), dummy_() {} Result(types::Err err_val) : is_ok_(false), err_value_(std::move(err_val.val)) {} Result(const Result& other) : is_ok_(other.is_ok_) { if (!is_ok_) { new (&err_value_) E(other.err_value_); } } Result(Result&& other) noexcept : is_ok_(other.is_ok_) { if (!is_ok_) { new (&err_value_) E(std::move(other.err_value_)); } } ~Result() { if (!is_ok_) { err_value_.~E(); } } bool is_ok() const { return is_ok_; } bool is_err() const { return !is_ok_; } void unwrap() const { if (!is_ok_) { std::fprintf(stderr, "Attempted to unwrap an error Result\n"); std::terminate(); } } const E& unwrap_err() const { if (is_ok_) { std::fprintf(stderr, "Attempted to unwrap_err on an ok Result\n"); std::terminate(); } return err_value_; } E& unwrap_err() { if (is_ok_) { std::fprintf(stderr, "Attempted to unwrap_err on an ok Result\n"); std::terminate(); } return err_value_; } }; #define match_result(res, ok_name, ok_block, err_name, err_block) \ if ((res).is_ok()) { \ auto&& ok_name = (res).unwrap(); \ ok_block \ } else { \ auto&& err_name = (res).unwrap_err(); \ err_block \ } #define if_let_err(res, name, block) \ if ((res).is_err()) { \ auto&& name = (res).unwrap_err(); \ block \ } #define if_let_ok(res, name, block) \ if ((res).is_ok()) { \ auto&& name = (res).unwrap(); \ block \ } } // namespace IdeviceFFI