namespace StevanFreeborn.Results { /// /// Static factory methods for creating result instances. /// public static class Result { /// /// Creates a successful result with the specified value. /// /// The type of the value. /// The type of the error. /// The value. /// A successful . public static Result Ok(T value) where TError : IError { return new Result(true, value, default); } /// /// Creates a failed result with the specified error. /// /// The type of the value. /// The type of the error. /// The error. /// A failed . public static Result Fail(TError error) where TError : IError { return new Result(false, default!, error); } /// /// Executes the specified function and wraps the result in a . /// /// The type of the value. /// The type of the error. /// The function to execute. /// A successful result with the return value if no exception is thrown; otherwise, a failed result. public static Result Try(Func func) where TError : IError { return Try(func, ex => (TError)(IError)new Error("UnexpectedError", ex.Message)); } /// /// Executes the specified function and wraps the result in a using the specified error handler. /// /// The type of the value. /// The type of the error. /// The function to execute. /// The function to convert exceptions to errors. /// A successful result with the return value if no exception is thrown; otherwise, a failed result. /// Thrown when func or errorHandler is null. public static Result Try(Func func, Func errorHandler) where TError : IError { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(func); #else if (func is null) { throw new ArgumentNullException(nameof(func)); } #endif #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(errorHandler); #else if (errorHandler is null) { throw new ArgumentNullException(nameof(errorHandler)); } #endif try { return Ok(func()); } catch (Exception ex) { return Fail(errorHandler(ex)); } } } /// /// Represents a result with a value and error type, used for operations that either succeed with a value or fail with an error. /// /// The type of the value. /// The type of the error. public sealed class Result where TError : IError { /// /// Gets a value indicating whether the result is successful. /// public bool IsSuccess { get; } /// /// Gets a value indicating whether the result is a failure. /// public bool IsFailure => !IsSuccess; /// /// Gets the value of the result. /// /// Thrown when accessing Value on a failed result. public T Value => IsSuccess ? _value : throw new InvalidOperationException($"Cannot access Value on a failed result. Error: [{Error.Code}] {Error.Message}"); /// /// Gets the error associated with the result. /// /// Thrown when accessing Error on a successful result. public TError Error => _error ?? throw new InvalidOperationException("Cannot access Error on a successful result."); private readonly T _value; private readonly TError? _error; internal Result(bool isSuccess, T value, TError? error) { IsSuccess = isSuccess; _value = value; _error = error; } /// /// Implicitly converts a value to a successful . /// /// The value to convert. public static implicit operator Result(T value) { return Result.Ok(value); } /// /// Implicitly converts an to a failed . /// /// The error to convert. public static implicit operator Result(TError error) { return Result.Fail(error); } /// /// Maps the value to a new type if the result is successful. /// /// The new type. /// The function to map the value. /// A new with the mapped value if successful, otherwise the current error. /// Thrown when mapper is null. public Result Map(Func mapper) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(mapper); #else if (mapper is null) { throw new ArgumentNullException(nameof(mapper)); } #endif return IsSuccess ? Result.Ok(mapper(Value)) : Result.Fail(Error); } /// /// Maps the error to a new error if the result is a failure. /// /// The function to map the error. /// A new with the mapped error if failed, otherwise the current result. /// Thrown when mapper is null. public Result MapError(Func mapper) where TNewError : IError { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(mapper); #else if (mapper is null) { throw new ArgumentNullException(nameof(mapper)); } #endif return IsFailure ? Result.Fail(mapper(Error)) : Result.Ok(Value); } /// /// Binds to a new result if the current result is successful. /// /// The new result type. /// The function to bind to on success. /// The result of the binder function if successful, otherwise the current error. /// Thrown when binder is null. public Result Bind(Func> binder) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(binder); #else if (binder is null) { throw new ArgumentNullException(nameof(binder)); } #endif return IsSuccess ? binder(Value) : Result.Fail(Error); } /// /// Matches the result and returns a value based on success or failure. /// /// The type of the result. /// The function to execute on success. /// The function to execute on failure. /// The result of the appropriate function. /// Thrown when onSuccess or onFailure is null. public TResult Match(Func onSuccess, Func onFailure) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(onSuccess); #else if (onSuccess is null) { throw new ArgumentNullException(nameof(onSuccess)); } #endif #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(onFailure); #else if (onFailure is null) { throw new ArgumentNullException(nameof(onFailure)); } #endif return IsSuccess ? onSuccess(Value) : onFailure(Error); } /// /// Matches the result and executes the appropriate action. /// /// The action to execute on success. /// The action to execute on failure. /// Thrown when onSuccess or onFailure is null. public void Match(Action onSuccess, Action onFailure) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(onSuccess); #else if (onSuccess is null) { throw new ArgumentNullException(nameof(onSuccess)); } #endif #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(onFailure); #else if (onFailure is null) { throw new ArgumentNullException(nameof(onFailure)); } #endif if (IsSuccess) { onSuccess(Value); } else { onFailure(Error); } } /// /// Executes the specified action if the result is successful, and returns the current result. /// /// The action to execute on success. /// The current result. /// Thrown when action is null. public Result Map(Action action) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(action); #else if (action is null) { throw new ArgumentNullException(nameof(action)); } #endif if (IsSuccess) { action(Value); } return this; } /// /// Executes the specified action if the result is successful, and returns the current result. /// /// The action to execute on success. /// The current result. /// Thrown when action is null. public Result Map(Action action) { #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(action); #else if (action is null) { throw new ArgumentNullException(nameof(action)); } #endif if (IsSuccess) { action(); } return this; } } }