-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add type-safe generics and improve error handling in ResultObject
- Introduced `IResult<TValue>` interface with `IsSuccess`, `IsFailure`, `Value`, and `Error` properties. - Removed `ValueOrDefault` in favor of nullable `Value` property. - Implemented `Cast<T>()` for type-safe value conversion with error propagation. - Removed exceptions for incorrect access of `Value` or `Error`. - Updated unit tests to align with new behavior and ensure correctness. - Bumped version from 1.0.2 to 1.0.3. - Refreshed README with new examples, API references, and behavior clarifications.
- Loading branch information
1 parent
539811c
commit 5c0558d
Showing
7 changed files
with
190 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Changelog | ||
|
||
## v1.0.3 | ||
|
||
1. `IResult`: | ||
- Introduced type-safe generics to `IResult`. | ||
2. `Result` Class Changes: | ||
- Removed the old `ValueOrDefault` feature in favor of nullable `Value` and `Error`. | ||
- Removed the enforced exception for accessing values or errors incorrectly. | ||
- Added a new `Cast<T>()` method for safe type casting with error preservation. | ||
- Deprecated `ToFailureResult<T>()` in favor of the new `Cast<T>()` method. | ||
3. Tests Updated: | ||
- Improved error handling validation in tests. | ||
- Removed exceptions where not necessary (like accessing `Value` on failure). | ||
- Updated tests to reflect the new `Cast<T>()` behavior. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
|
||
namespace ResultObject; | ||
|
||
/// <summary> | ||
/// Represents a result of an operation, providing information about success or failure. | ||
/// </summary> | ||
public interface IResult<out TValue> | ||
{ | ||
/// <summary> | ||
/// Gets a value indicating whether the operation was successful. | ||
/// </summary> | ||
[MemberNotNullWhen(true, nameof(Value))] | ||
[MemberNotNullWhen(false, nameof(Error))] | ||
bool IsSuccess { get; } | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether the operation failed. | ||
/// </summary> | ||
[MemberNotNullWhen(true, nameof(Error))] | ||
[MemberNotNullWhen(false, nameof(Value))] | ||
bool IsFailure { get; } | ||
|
||
/// <summary> | ||
/// Gets the value returned by the operation if it was successful. | ||
/// </summary> | ||
TValue? Value { get; } | ||
|
||
/// <summary> | ||
/// Gets the error information associated with the failure, if any. | ||
/// </summary> | ||
ResultError? Error { get; } | ||
|
||
/// <summary> | ||
/// Casts the value of this result to a new type if the result is successful. | ||
/// </summary> | ||
/// <typeparam name="T">The new type to cast the value to.</typeparam> | ||
/// <returns> | ||
/// A new <see cref="Result{T}"/> with the value cast to the specified type, or the same error if the result was a failure. | ||
/// </returns> | ||
/// <exception cref="InvalidCastException">Thrown if the value cannot be cast to the specified type.</exception> | ||
IResult<T> Cast<T>(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,67 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
|
||
namespace ResultObject; | ||
|
||
/// <summary> | ||
/// Represents the result of an operation, which can either be a success with a value or a failure with an error. | ||
/// Represents the result of an operation, which can either be a success or a failure. | ||
/// </summary> | ||
/// <typeparam name="TValue">The type of the value associated with a successful result.</typeparam> | ||
public class Result<TValue>(TValue? value, ResultError? error) | ||
/// <typeparam name="TValue">The type of the value contained in the result when it is successful.</typeparam> | ||
public class Result<TValue>(TValue? value, ResultError? error) : IResult<TValue> | ||
{ | ||
/// <summary> | ||
/// Gets a value indicating whether the result represents a successful outcome (i.e., no error occurred). | ||
/// Gets a value indicating whether the result represents a success. | ||
/// </summary> | ||
public bool IsSuccess { get; } = error == null; | ||
/// <remarks> | ||
/// This property is <c>true</c> if the <see cref="Value"/> is not <c>null</c> and there is no error. | ||
/// </remarks> | ||
[MemberNotNullWhen(true, nameof(Value))] | ||
[MemberNotNullWhen(false, nameof(Error))] | ||
public bool IsSuccess { get; } = error == null && value != null; | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether the result represents a failure (i.e., an error occurred). | ||
/// Gets a value indicating whether the result represents a failure. | ||
/// </summary> | ||
public bool IsFailure { get; } = error != null; | ||
/// <remarks> | ||
/// This property is <c>true</c> if the <see cref="Error"/> is not <c>null</c>. | ||
/// </remarks> | ||
[MemberNotNullWhen(true, nameof(Error))] | ||
[MemberNotNullWhen(false, nameof(Value))] | ||
public bool IsFailure { get; } = error != null || value == null; | ||
|
||
/// <summary> | ||
/// Gets the value of a successful result. | ||
/// Throws an exception if the result represents a failure or if the value is null even though the result is marked as successful. | ||
/// Gets the value of the result if it is a success; otherwise, <c>null</c>. | ||
/// </summary> | ||
/// <exception cref="InvalidOperationException"> | ||
/// Thrown when trying to access the value of a failed result, or when the result is successful but the value is null. | ||
/// </exception> | ||
public TValue Value | ||
{ | ||
get | ||
{ | ||
if (IsFailure) | ||
{ | ||
throw new InvalidOperationException("Cannot retrieve value from a failed result."); | ||
} | ||
|
||
if (value == null) | ||
{ | ||
throw new InvalidOperationException("Value is null despite the operation being successful."); | ||
} | ||
|
||
return value; | ||
} | ||
} | ||
public TValue? Value => value; | ||
|
||
/// <summary> | ||
/// Gets the value if the result is successful, or the default value for the type <typeparamref name="TValue"/> if the result is a failure. | ||
/// Gets the error associated with the result if it is a failure; otherwise, <c>null</c>. | ||
/// </summary> | ||
public TValue? ValueOrDefault => value; | ||
public ResultError? Error => error; | ||
|
||
/// <summary> | ||
/// Gets the error associated with a failed result. | ||
/// Throws an exception if accessed on a successful result. | ||
/// Casts the value of this result to a new type if the result is successful. | ||
/// </summary> | ||
/// <exception cref="InvalidOperationException"> | ||
/// Thrown when trying to access the error of a successful result. | ||
/// </exception> | ||
public ResultError Error | ||
/// <typeparam name="T">The new type to cast the value to.</typeparam> | ||
/// <returns> | ||
/// A new <see cref="Result{T}"/> with the value cast to the specified type, or the same error if the result was a failure. | ||
/// </returns> | ||
/// <exception cref="InvalidCastException">Thrown if the value cannot be cast to the specified type.</exception> | ||
public IResult<T> Cast<T>() | ||
{ | ||
get | ||
if (IsFailure) | ||
{ | ||
if (IsSuccess) | ||
{ | ||
throw new InvalidOperationException("Cannot retrieve error from a successful result."); | ||
} | ||
|
||
return error!; | ||
// If it's a failure result, return a failure with the existing error. | ||
return new Result<T>(default, Error); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets the error if the result is a failure, or null if the result is successful. | ||
/// </summary> | ||
public ResultError? ErrorOrDefault => error; | ||
if (value is T castValue) | ||
{ | ||
// Successfully cast the value to the target type T. | ||
return new Result<T>(castValue, null); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new <c>Result</c> instance representing the same failure as this result, | ||
/// but with the value type cast to a different type. | ||
/// The new result will contain the same error, while the value will be set to the default value of the new type. | ||
/// </summary> | ||
/// <typeparam name="T">The new type for the value in the returned <c>Result</c>.</typeparam> | ||
/// <returns>A new <c>Result</c> instance representing the failure, with the value type changed to <typeparamref name="T"/>.</returns> | ||
public Result<T> ToFailureResult<T>() => new(default, Error); | ||
// The value could not be cast to the target type. | ||
throw new InvalidCastException( | ||
$"Cannot cast value of type {typeof(TValue).FullName} to {typeof(T).FullName}."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.