Files
vdm/core/include/vdm/util/result.hpp
T
samiandClaude Sonnet 5 a40585f419 core: clang-format pass against the landed root .clang-format
Pure formatting, no behaviour change. PKG landed .clang-format (Google
base, 4-space indent, 100 cols); this brings util/ and the test harness
into conformance so `clang-format --dry-run -Werror` is clean. Build and
all six test binaries unchanged and green.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
2026-09-09 19:11:21 +04:00

161 lines
5.6 KiB
C++

// vdm/util/result.hpp — the return-based error channel for the transfer path.
//
// Errors are returned, never thrown, on anything that runs during a download
// (AGENT-CORE brief). Result<T> is a thin wrapper over std::expected<T, ErrorInfo>
// so we keep a stable spelling and a few helpers; the monadic surface forwards to
// std::expected.
//
// This header compiles standalone.
#ifndef VDM_UTIL_RESULT_HPP
#define VDM_UTIL_RESULT_HPP
#include <expected>
#include <string>
#include <type_traits>
#include <utility>
#include "vdm/util/error.hpp"
namespace vdm {
// Alias for the "failure" side, so call sites read `return Err{Error::timeout, "..."}`.
using Err = ErrorInfo;
template <class T>
class [[nodiscard]] Result {
using storage = std::expected<T, ErrorInfo>;
storage exp_;
public:
using value_type = T;
using error_type = ErrorInfo;
// Success construction: implicit from a T (or anything convertible to T).
template <class U = T,
class = std::enable_if_t<std::is_constructible_v<T, U &&> &&
!std::is_same_v<std::remove_cvref_t<U>, Result>>>
Result(U &&value) : exp_(std::in_place, std::forward<U>(value)) {}
Result()
requires std::is_default_constructible_v<T>
: exp_(std::in_place) {}
// Failure construction: implicit from an ErrorInfo/Err.
Result(ErrorInfo error) : exp_(std::unexpected(std::move(error))) {}
Result(Error code) : exp_(std::unexpected(ErrorInfo(code))) {}
[[nodiscard]] bool has_value() const noexcept { return exp_.has_value(); }
explicit operator bool() const noexcept { return exp_.has_value(); }
T &value() & { return exp_.value(); }
const T &value() const & { return exp_.value(); }
T &&value() && { return std::move(exp_).value(); }
T *operator->() noexcept { return &*exp_; }
const T *operator->() const noexcept { return &*exp_; }
T &operator*() & noexcept { return *exp_; }
const T &operator*() const & noexcept { return *exp_; }
T &&operator*() && noexcept { return *std::move(exp_); }
const ErrorInfo &error() const & { return exp_.error(); }
ErrorInfo &&error() && { return std::move(exp_).error(); }
[[nodiscard]] Error code() const noexcept {
return exp_.has_value() ? Error::ok : exp_.error().code;
}
template <class U>
T value_or(U &&fallback) const & {
return exp_.value_or(std::forward<U>(fallback));
}
// Monadic forwarding — see std::expected. `and_then` chains Result-returning
// callables; `transform` maps the value; `transform_error` rewrites the failure.
template <class F>
auto and_then(F &&f) & {
return exp_.and_then(std::forward<F>(f));
}
template <class F>
auto and_then(F &&f) const & {
return exp_.and_then(std::forward<F>(f));
}
template <class F>
auto and_then(F &&f) && {
return std::move(exp_).and_then(std::forward<F>(f));
}
template <class F>
auto transform(F &&f) & {
return exp_.transform(std::forward<F>(f));
}
template <class F>
auto transform(F &&f) const & {
return exp_.transform(std::forward<F>(f));
}
template <class F>
auto transform(F &&f) && {
return std::move(exp_).transform(std::forward<F>(f));
}
template <class F>
auto transform_error(F &&f) const & {
return exp_.transform_error(std::forward<F>(f));
}
};
// Result<void> — success carries nothing; failure still carries ErrorInfo.
template <>
class [[nodiscard]] Result<void> {
std::expected<void, ErrorInfo> exp_;
public:
using value_type = void;
using error_type = ErrorInfo;
Result() : exp_() {}
Result(ErrorInfo error) : exp_(std::unexpected(std::move(error))) {}
Result(Error code) : exp_(std::unexpected(ErrorInfo(code))) {}
[[nodiscard]] bool has_value() const noexcept { return exp_.has_value(); }
explicit operator bool() const noexcept { return exp_.has_value(); }
void value() const { exp_.value(); }
const ErrorInfo &error() const & { return exp_.error(); }
ErrorInfo &&error() && { return std::move(exp_).error(); }
[[nodiscard]] Error code() const noexcept {
return exp_.has_value() ? Error::ok : exp_.error().code;
}
};
// Explicit success sentinel for Result<void> returns that reads better than `return {}`.
inline Result<void> ok() {
return {};
}
} // namespace vdm
#define VDM_DETAIL_CAT_(a, b) a##b
#define VDM_DETAIL_CAT(a, b) VDM_DETAIL_CAT_(a, b)
// VDM_TRY(expr): evaluate a Result-returning expression; on failure, return its error
// from the enclosing function (which must itself return a Result<...>). On success the
// macro yields nothing — use VDM_TRY_ASSIGN to bind the value.
#define VDM_TRY(expr) \
do { \
auto _vdm_r = (expr); \
if (!_vdm_r.has_value()) \
return ::vdm::ErrorInfo(std::move(_vdm_r).error()); \
} while (0)
// VDM_TRY_ASSIGN(decl, expr): bind `decl` to the value of a successful Result, else
// return its error. Usage: VDM_TRY_ASSIGN(auto n, read_some());
#define VDM_TRY_ASSIGN(decl, expr) \
auto VDM_DETAIL_CAT(_vdm_tmp_, __LINE__) = (expr); \
if (!VDM_DETAIL_CAT(_vdm_tmp_, __LINE__).has_value()) \
return ::vdm::ErrorInfo(std::move(VDM_DETAIL_CAT(_vdm_tmp_, __LINE__)).error()); \
decl = *std::move(VDM_DETAIL_CAT(_vdm_tmp_, __LINE__))
#endif // VDM_UTIL_RESULT_HPP