Files
Just.Railway/Railway/Result.cs
JustFixMe e909eeae10
All checks were successful
.NET Test / test (push) Successful in 1m10s
extended Ensure api
2024-02-11 00:11:14 +04:00

287 lines
9.9 KiB
C#

namespace Just.Railway;
internal enum ResultState : byte
{
Bottom = 0, Error = 0b01, Success = 0b11,
}
public readonly partial struct Result : IEquatable<Result>
{
internal SuccessUnit Value => new();
internal readonly Error? Error;
internal readonly ResultState State;
internal Result(Error? error)
{
Error = error;
State = error is null ? ResultState.Success : ResultState.Error;
}
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result Success() => new(null);
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<T> Success<T>(T value) => new(value);
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<(T1, T2)> Success<T1, T2>(T1 value1, T2 value2) => new((value1, value2));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<(T1, T2, T3)> Success<T1, T2, T3>(T1 value1, T2 value2, T3 value3) => new((value1, value2, value3));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<(T1, T2, T3, T4)> Success<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4) => new((value1, value2, value3, value4));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<(T1, T2, T3, T4, T5)> Success<T1, T2, T3, T4, T5>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) => new((value1, value2, value3, value4, value5));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result Failure(Error error) => new(error ?? throw new ArgumentNullException(nameof(error)));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result Failure(Exception exception) => new(Error.New(exception) ?? throw new ArgumentNullException(nameof(exception)));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<T> Failure<T>(Error error) => new(error ?? throw new ArgumentNullException(nameof(error)));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Result<T> Failure<T>(Exception exception) => new(Error.New(exception) ?? throw new ArgumentNullException(nameof(exception)));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result(Error error) => new(error ?? throw new ArgumentNullException(nameof(error)));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result(Exception exception) => new(
new ExceptionalError(exception ?? throw new ArgumentNullException(nameof(exception))));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result<SuccessUnit>(Result result) => result.State switch
{
ResultState.Success => new(new SuccessUnit()),
ResultState.Error => new(result.Error!),
_ => throw new ResultNotInitializedException(nameof(result))
};
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Result(SuccessUnit _) => new(null);
[Pure] public bool IsSuccess => Error is null;
[Pure] public bool IsFailure => Error is not null;
[Pure] public bool Success([MaybeNullWhen(false)]out SuccessUnit? u, [MaybeNullWhen(true), NotNullWhen(false)]out Error? error)
{
switch (State)
{
case ResultState.Success:
u = new SuccessUnit();
error = default;
return true;
case ResultState.Error:
u = default;
error = Error!;
return false;
default: throw new ResultNotInitializedException();
}
}
[Pure] public bool TryGetError([MaybeNullWhen(false)]out Error error)
{
if (IsSuccess)
{
error = default;
return false;
}
if (IsFailure)
{
error = Error!;
return true;
}
throw new ResultNotInitializedException();
}
[Pure] public override string ToString() => State switch
{
ResultState.Success => "",
ResultState.Error => Error!.ToString(),
_ => throw new ResultNotInitializedException()
};
[Pure] public override int GetHashCode() => State switch
{
ResultState.Success => 0,
ResultState.Error => Error!.GetHashCode(),
_ => throw new ResultNotInitializedException()
};
[Pure] public override bool Equals(object? obj) => obj is Result other && Equals(other);
[Pure] public bool Equals(Result other)
{
if (State == ResultState.Bottom)
throw new ResultNotInitializedException();
return Error == other.Error;
}
[Pure] public static bool operator ==(Result left, Result right) => left.Equals(right);
[Pure] public static bool operator !=(Result left, Result right) => !(left == right);
}
public readonly struct Result<T> : IEquatable<Result<T>>
{
internal readonly Error? Error;
internal readonly T Value;
internal readonly ResultState State;
internal Result(Error error)
{
Error = error ?? throw new ArgumentNullException(nameof(error));
State = ResultState.Error;
Value = default!;
}
internal Result(T value)
{
Value = value;
State = ResultState.Success;
Error = default;
}
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Result(Result<T> result) => result.State switch
{
ResultState.Success => new(null),
ResultState.Error => new(result.Error!),
_ => throw new ResultNotInitializedException(nameof(result))
};
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result<T>(Error error) => new(error);
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result<T>(Exception exception) => new(
new ExceptionalError(exception ?? throw new ArgumentNullException(nameof(exception))));
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Result<T>(T value) => new(value);
[Pure] public bool IsSuccess => State == ResultState.Success;
[Pure] public bool IsFailure => State == ResultState.Error;
[Pure] public bool Success([MaybeNullWhen(false)]out T value, [MaybeNullWhen(true), NotNullWhen(false)]out Error? error)
{
switch (State)
{
case ResultState.Success:
value = Value;
error = default;
return true;
case ResultState.Error:
value = default;
error = Error!;
return false;
default: throw new ResultNotInitializedException();
}
}
[Pure] public bool TryGetValue([MaybeNullWhen(false)]out T value)
{
switch (State)
{
case ResultState.Success:
value = Value;
return true;
case ResultState.Error:
value = default;
return false;
default: throw new ResultNotInitializedException();
}
}
[Pure] public bool TryGetError([MaybeNullWhen(false)]out Error error)
{
switch (State)
{
case ResultState.Success:
error = default;
return false;
case ResultState.Error:
error = Error!;
return true;
default: throw new ResultNotInitializedException();
}
}
[Pure] public Result<R> Cast<R>()
{
switch (State)
{
case ResultState.Error:
return Error!;
case ResultState.Success:
{
if (Value is R ret)
return ret;
if (typeof(R).IsAssignableFrom(typeof(T)) && Value is null)
return default(R)!;
return (R)(object)Value!;
}
default: throw new ResultNotInitializedException();
}
}
[Pure] public override string ToString() => State switch
{
ResultState.Success => Value?.ToString() ?? "",
ResultState.Error => Error!.ToString(),
_ => throw new ResultNotInitializedException()
};
[Pure] public override int GetHashCode() => State switch
{
ResultState.Success => Value?.GetHashCode() ?? 0,
ResultState.Error => Error!.GetHashCode(),
_ => throw new ResultNotInitializedException()
};
[Pure] public override bool Equals(object? obj) => obj is Result<T> other && Equals(other);
[Pure] public bool Equals(Result<T> other)
{
if (State == ResultState.Bottom)
throw new ResultNotInitializedException();
if (IsSuccess != other.IsSuccess)
return false;
return IsSuccess
? ReflectionHelper.IsEqual(Value, other.Value)
: Error == other.Error;
}
[Pure] public static bool operator ==(Result<T> left, Result<T> right) => left.Equals(right);
[Pure] public static bool operator !=(Result<T> left, Result<T> right) => !(left == right);
}
public readonly struct SuccessUnit : IEquatable<SuccessUnit>
{
public override bool Equals([NotNullWhen(true)] object? obj) => obj is SuccessUnit;
public bool Equals(SuccessUnit other) => true;
public override int GetHashCode() => 0;
public override string ToString() => "success";
public static bool operator ==(SuccessUnit left, SuccessUnit right) => left.Equals(right);
public static bool operator !=(SuccessUnit left, SuccessUnit right) => !(left == right);
}
[Serializable]
public class ResultNotInitializedException : InvalidOperationException
{
public ResultNotInitializedException(string variableName = "this")
: base("Result was not properly initialized.")
{
VariableName = variableName;
}
public string VariableName { get; }
}