#ifndef FESA_CORE_STATUS_H_ #define FESA_CORE_STATUS_H_ #include #include #include #include #include "fesa/core/diagnostic.h" namespace fesa { /// @brief Maps failures to the stable command-line exit-code categories. enum class FailureCategory { kInput, kModel, kSolver, kOutput }; /// @brief Transports success or structured failure diagnostics. class Status { public: /// @brief Creates a successful status. /// @return A status with no failure category or diagnostics. static Status Ok(); /// @brief Creates an uncategorized failed status. /// @param diagnostics Structured diagnostics owned by the returned status. /// @return A failed status with diagnostics in deterministic source order. static Status Failure(std::vector diagnostics); /// @brief Creates a categorized failed status. /// @param category Stable external failure category. /// @param diagnostics Structured diagnostics owned by the returned status. /// @return A failed status with diagnostics in deterministic source order. static Status Failure(FailureCategory category, std::vector diagnostics); /// @brief Reports whether the operation succeeded. bool IsOk() const noexcept; /// @brief Returns the optional stable failure category. std::optional Category() const noexcept; /// @brief Returns the deterministically ordered diagnostic records. const std::vector& Diagnostics() const noexcept; private: /// @brief Constructs a status from its validated invariant fields. Status(bool is_ok, std::optional category, std::vector diagnostics); bool is_ok_; std::optional category_; std::vector diagnostics_; }; /// @brief Owns exactly one successful value or one failed Status. template class Result { public: /// @brief Creates a successful result that owns the supplied value. static Result Success(T value) { return Result{SuccessTag{}, std::move(value)}; } /// @brief Creates a failed result that owns a failed status. /// @throws std::invalid_argument if status represents success. static Result Failure(Status status) { if (status.IsOk()) { throw std::invalid_argument{"A failed Result requires a failed Status."}; } return Result{FailureTag{}, std::move(status)}; } /// @brief Reports whether this result owns a successful value. bool HasValue() const noexcept { return value_.has_value(); } /// @brief Returns the owned successful value. /// @throws std::logic_error if this result represents failure. T& Value() { if (!value_) { throw std::logic_error{"Result has no value."}; } return *value_; } /// @brief Returns the owned successful value. /// @throws std::logic_error if this result represents failure. const T& Value() const { if (!value_) { throw std::logic_error{"Result has no value."}; } return *value_; } /// @brief Returns the success or failure status. const Status& GetStatus() const noexcept { return status_; } private: struct SuccessTag {}; struct FailureTag {}; /// @brief Constructs the successful value alternative. Result(SuccessTag, T value) : value_{std::move(value)}, status_{Status::Ok()} {} /// @brief Constructs the failed status alternative. Result(FailureTag, Status status) : value_{std::nullopt}, status_{std::move(status)} {} std::optional value_; Status status_; }; } // namespace fesa #endif // FESA_CORE_STATUS_H_