// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "UnsyncCommon.h" #include "UnsyncLog.h" #include #include #include #include #include namespace unsync { enum class EErrorKind { Unknown, App, System, Http, }; struct FError { int32 Code = 0; EErrorKind Kind = EErrorKind::Unknown; std::wstring Context; // TODO: add support for chained errors for rich error context reporting }; inline FError SystemError(std::wstring&& Context, int32 Code = -1) { return FError{.Code = Code, .Kind = EErrorKind::System, .Context = Context}; } inline FError AppError(std::wstring&& Context, int32 Code = -1) { return FError{.Code = Code, .Kind = EErrorKind::App, .Context = Context}; } inline FError AppError(std::string&& Context, int32 Code = -1) { extern std::wstring ConvertUtf8ToWide(std::string_view StringUtf8); return FError{.Code = Code, .Kind = EErrorKind::App, .Context = ConvertUtf8ToWide(Context)}; } inline FError HttpError(std::wstring&& Context, int32 Code) { return FError{.Code = Code, .Kind = EErrorKind::Http, .Context = Context}; } inline FError HttpError(std::string&& Context, int32 Code) { extern std::wstring ConvertUtf8ToWide(std::string_view StringUtf8); return HttpError(ConvertUtf8ToWide(Context), Code); } inline FError HttpError(int32 Code) { return FError{.Code = Code, .Kind = EErrorKind::Http}; } struct FEmpty { }; template struct [[nodiscard]] TResult { // Implicit construction from Error type for convenience TResult(Et&& E) : DataOrError(std::forward(E)) {} explicit TResult(T&& Ok) : DataOrError(std::forward(Ok)) {} bool IsOk() const { return std::holds_alternative(DataOrError); } bool IsError() const { return std::holds_alternative(DataOrError); } const T* TryData() const { return std::get_if(&DataOrError); } const Et* TryError() const { return std::get_if(&DataOrError); } T* TryData() { return std::get_if(&DataOrError); } Et* TryError() { return std::get_if(&DataOrError); } const T& GetData() const { UNSYNC_ASSERT(IsOk()); return *TryData(); } const Et& GetError() const { UNSYNC_ASSERT(IsError()); return *TryError(); } T& GetData() { UNSYNC_ASSERT(IsOk()); return *TryData(); } Et& GetError() { UNSYNC_ASSERT(IsError()); return *TryError(); } const T* operator->() const { UNSYNC_ASSERT(IsOk()); return TryData(); } const T& operator*() const { UNSYNC_ASSERT(IsOk()); return *TryData(); } private: std::variant DataOrError; }; template inline TResult MoveError(TResult& X) { return TResult(std::move(X.GetError())); } template static TResult ResultOk(Tx Ok) { return TResult(std::forward(Ok)); } template static TResult ResultOk() { return TResult(FEmpty()); } struct FAtomicError { std::atomic_flag Flag; std::optional Data; operator bool() const { return Test(); } bool Test() const { return Flag.test(); } bool Set(FError&& InData) { if (Flag.test_and_set() == false) { Data = std::move(InData); return true; } else { return false; } } }; #define UNSYNC_RETURN_ON_ERROR(x) \ { \ if (x.IsError()) \ { \ return std::move(x.GetError()); \ } \ } } // namespace unsync