Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Migrate from Errors to Exceptions for invalid use of library #105

Merged
merged 13 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading