Skip to content

Commit

Permalink
Merge pull request #106 from feO2x/exceptions-for-invalid-use
Browse files Browse the repository at this point in the history
Exceptions for invalid use - review and extensions
  • Loading branch information
amantinband authored May 12, 2024
2 parents a76df26 + 61d1540 commit 7f4bbe3
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 144 deletions.
6 changes: 6 additions & 0 deletions src/ErrorOr/EmptyErrors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ErrorOr;

internal static class EmptyErrors
{
public static List<Error> Instance { get; } = [];
}
106 changes: 60 additions & 46 deletions src/ErrorOr/ErrorOr.ImplicitConverters.cs
Original file line number Diff line number Diff line change
@@ -1,46 +1,60 @@
namespace ErrorOr;

public readonly partial record struct ErrorOr<TValue> : IErrorOr<TValue>
{
/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a value.
/// </summary>
public static implicit operator ErrorOr<TValue>(TValue value)
{
return new ErrorOr<TValue>(value);
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from an error.
/// </summary>
public static implicit operator ErrorOr<TValue>(Error error)
{
return new ErrorOr<TValue>(error);
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a list of errors.
/// </summary>
public static implicit operator ErrorOr<TValue>(List<Error> errors)
{
if (errors.Count == 0)
{
throw new InvalidOperationException("Cannot create an ErrorOr<TValue> from an empty list of errors. Provide at least one error.");
}

return new ErrorOr<TValue>(errors);
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a list of errors.
/// </summary>
public static implicit operator ErrorOr<TValue>(Error[] errors)
{
if (errors.Length == 0)
{
throw new InvalidOperationException("Cannot create an ErrorOr<TValue> from an empty array of errors. Provide at least one error.");
}

return new ErrorOr<TValue>(errors.ToList());
}
}
namespace ErrorOr;

public readonly partial record struct ErrorOr<TValue> : IErrorOr<TValue>
{
/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a value.
/// </summary>
public static implicit operator ErrorOr<TValue>(TValue value)
{
return new ErrorOr<TValue>(value);
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from an error.
/// </summary>
public static implicit operator ErrorOr<TValue>(Error error)
{
return new ErrorOr<TValue>(error);
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a list of errors.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="errors"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="errors" /> is an empty list.</exception>
public static implicit operator ErrorOr<TValue>(List<Error> errors)
{
if (errors is null)
{
throw new ArgumentNullException(nameof(errors));
}

if (errors.Count == 0)
{
throw new ArgumentException("Cannot create an ErrorOr<TValue> from an empty list of errors. Provide at least one error.", nameof(errors));
}

return new ErrorOr<TValue>(errors);
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a list of errors.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="errors"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="errors" /> is an empty array.</exception>
public static implicit operator ErrorOr<TValue>(Error[] errors)
{
if (errors is null)
{
throw new ArgumentNullException(nameof(errors));
}

if (errors.Length == 0)
{
throw new ArgumentException("Cannot create an ErrorOr<TValue> from an empty array of errors. Provide at least one error.", nameof(errors));
}

return new ErrorOr<TValue>(errors.ToList());
}
}
18 changes: 15 additions & 3 deletions src/ErrorOr/ErrorOr.ToErrorOrExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ public static ErrorOr<TValue> ToErrorOr<TValue>(this Error error)
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> instance with the given <paramref name="error"/>.
/// Creates an <see cref="ErrorOr{TValue}"/> instance with the given <paramref name="errors"/>.
/// </summary>
public static ErrorOr<TValue> ToErrorOr<TValue>(this List<Error> error)
/// <exception cref="ArgumentNullException">Thrown when <paramref name="errors"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="errors" /> is an empty list.</exception>
public static ErrorOr<TValue> ToErrorOr<TValue>(this List<Error> errors)
{
return error;
return errors;
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> instance with the given <paramref name="errors"/>.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="errors"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="errors" /> is an empty array.</exception>
public static ErrorOr<TValue> ToErrorOr<TValue>(this Error[] errors)
{
return errors;
}
}
186 changes: 99 additions & 87 deletions src/ErrorOr/ErrorOr.cs
Original file line number Diff line number Diff line change
@@ -1,87 +1,99 @@
using System.Diagnostics.CodeAnalysis;

namespace ErrorOr;

/// <summary>
/// A discriminated union of errors or a value.
/// </summary>
/// <typeparam name="TValue">The type of the underlying <see cref="Value"/>.</typeparam>
public readonly partial record struct ErrorOr<TValue> : IErrorOr<TValue>
{
private readonly TValue? _value = default;
private readonly List<Error>? _errors = null;

/// <summary>
/// Prevents a default <see cref="ErrorOr"/> struct from being created.
/// </summary>
public ErrorOr()
{
throw new InvalidOperationException("Default construction of ErrorOr<TValue> is invalid. Please use provided factory methods to instantiate.");
}

private ErrorOr(Error error)
{
_errors = [error];
IsError = true;
}

private ErrorOr(List<Error> errors)
{
_errors = errors;
IsError = true;
}

private ErrorOr(TValue value)
{
_value = value;
IsError = false;
}

/// <summary>
/// Gets a value indicating whether the state is error.
/// </summary>
[MemberNotNullWhen(true, nameof(_errors))]
[MemberNotNullWhen(true, nameof(Errors))]
[MemberNotNullWhen(false, nameof(Value))]
[MemberNotNullWhen(false, nameof(_value))]
public bool IsError { get; }

/// <summary>
/// Gets the list of errors. If the state is not error, the list will contain a single error representing the state.
/// </summary>
public List<Error> Errors => IsError ? _errors! : throw new InvalidOperationException("The Errors property cannot be accessed when no errors have been recorded. Check IsError before accessing Errors.");

/// <summary>
/// Gets the list of errors. If the state is not error, the list will be empty.
/// </summary>
public List<Error> ErrorsOrEmptyList => IsError ? _errors! : [];

/// <summary>
/// Gets the value.
/// </summary>
public TValue Value => _value!;

/// <summary>
/// Gets the first error.
/// </summary>
public Error FirstError
{
get
{
if (!IsError)
{
throw new InvalidOperationException("The FirstError property cannot be accessed when no errors have been recorded. Check IsError before accessing FirstError.");
}

return _errors![0];
}
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a list of errors.
/// </summary>
public static ErrorOr<TValue> From(List<Error> errors)
{
return errors;
}
}
using System.Diagnostics.CodeAnalysis;

namespace ErrorOr;

/// <summary>
/// A discriminated union of errors or a value.
/// </summary>
/// <typeparam name="TValue">The type of the underlying <see cref="Value"/>.</typeparam>
public readonly partial record struct ErrorOr<TValue> : IErrorOr<TValue>
{
private readonly TValue? _value = default;
private readonly List<Error>? _errors = null;

/// <summary>
/// Prevents a default <see cref="ErrorOr"/> struct from being created.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when this method is called.</exception>
public ErrorOr()
{
throw new InvalidOperationException("Default construction of ErrorOr<TValue> is invalid. Please use provided factory methods to instantiate.");
}

private ErrorOr(Error error)
{
_errors = [error];
}

private ErrorOr(List<Error> errors)
{
_errors = errors;
}

private ErrorOr(TValue value)
{
_value = value;
}

/// <summary>
/// Gets a value indicating whether the state is error.
/// </summary>
[MemberNotNullWhen(true, nameof(_errors))]
[MemberNotNullWhen(true, nameof(Errors))]
[MemberNotNullWhen(false, nameof(Value))]
[MemberNotNullWhen(false, nameof(_value))]
public bool IsError => _errors is not null;

/// <summary>
/// Gets the list of errors. If the state is not error, the list will contain a single error representing the state.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when no errors are present.</exception>
public List<Error> Errors => IsError ? _errors : throw new InvalidOperationException("The Errors property cannot be accessed when no errors have been recorded. Check IsError before accessing Errors.");

/// <summary>
/// Gets the list of errors. If the state is not error, the list will be empty.
/// </summary>
public List<Error> ErrorsOrEmptyList => IsError ? _errors : EmptyErrors.Instance;

/// <summary>
/// Gets the value.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when no value is present.</exception>
public TValue Value
{
get
{
if (IsError)
{
throw new InvalidOperationException("The Value property cannot be accessed when errors have been recorded. Check IsError before accessing Value.");
}

return _value;
}
}

/// <summary>
/// Gets the first error.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when no errors are present.</exception>
public Error FirstError
{
get
{
if (!IsError)
{
throw new InvalidOperationException("The FirstError property cannot be accessed when no errors have been recorded. Check IsError before accessing FirstError.");
}

return _errors[0];
}
}

/// <summary>
/// Creates an <see cref="ErrorOr{TValue}"/> from a list of errors.
/// </summary>
public static ErrorOr<TValue> From(List<Error> errors)
{
return errors;
}
}
Loading

0 comments on commit 7f4bbe3

Please sign in to comment.