@@ -2,6 +2,7 @@ namespace Just.Railway;
|
||||
|
||||
public static partial class Ensure
|
||||
{
|
||||
[return: NotNull]
|
||||
public delegate Error ErrorFactory(string valueExpression);
|
||||
|
||||
public const string DefaultErrorType = "EnsureFailed";
|
||||
|
||||
+13
-7
@@ -14,7 +14,13 @@ public abstract class Error : IEquatable<Error>, IComparable<Error>
|
||||
/// </summary>
|
||||
/// <param name="thisException">Exception</param>
|
||||
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Error New(Exception thisException) => new ExceptionalError(thisException);
|
||||
public static Error New(Exception thisException) => thisException switch
|
||||
{
|
||||
AggregateException { InnerExceptions.Count: > 1 } aggegateException => new ManyErrors(aggegateException.InnerExceptions.Select(x => Error.New(x))),
|
||||
AggregateException { InnerExceptions.Count: 1 } aggegateException => new ExceptionalError(aggegateException.InnerExceptions[0]),
|
||||
_ => new ExceptionalError(thisException),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="ExceptionalError"/> with an overriden detail. This can be useful for sanitising the display message
|
||||
/// when internally we're carrying the exception.
|
||||
@@ -85,7 +91,7 @@ public abstract class Error : IEquatable<Error>, IComparable<Error>
|
||||
[Pure] public abstract int Count { get; }
|
||||
[Pure] public abstract bool IsEmpty { get; }
|
||||
[Pure] public abstract bool IsExpected { get; }
|
||||
[Pure] public abstract bool IsExeptional { get; }
|
||||
[Pure] public abstract bool IsExceptional { get; }
|
||||
|
||||
[Pure] public Error Append(Error? next)
|
||||
{
|
||||
@@ -166,7 +172,7 @@ public sealed class ExpectedError : Error
|
||||
[Pure] public override int Count => 1;
|
||||
[Pure] public override bool IsEmpty => false;
|
||||
[Pure] public override bool IsExpected => true;
|
||||
[Pure] public override bool IsExeptional => false;
|
||||
[Pure] public override bool IsExceptional => false;
|
||||
|
||||
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override IEnumerable<Error> ToEnumerable()
|
||||
@@ -208,7 +214,7 @@ public sealed class ExceptionalError : Error
|
||||
[Pure] public override int Count => 1;
|
||||
[Pure] public override bool IsEmpty => false;
|
||||
[Pure] public override bool IsExpected => false;
|
||||
[Pure] public override bool IsExeptional => true;
|
||||
[Pure] public override bool IsExceptional => true;
|
||||
|
||||
[Pure] public override Exception ToException() => Exception ?? base.ToException();
|
||||
|
||||
@@ -220,7 +226,7 @@ public sealed class ExceptionalError : Error
|
||||
|
||||
private static ImmutableDictionary<string, string> ExtractExtensionData(Exception exception)
|
||||
{
|
||||
if (!(exception.Data?.Count > 0))
|
||||
if (exception.Data is null || exception.Data.Count == 0)
|
||||
return ImmutableDictionary<string, string>.Empty;
|
||||
|
||||
var values = GetGenericExtData(exception);
|
||||
@@ -276,7 +282,7 @@ public sealed class ManyErrors : Error, IEnumerable<Error>, IReadOnlyList<Error>
|
||||
{
|
||||
var unpackedErrors = ImmutableArray.CreateBuilder<Error>();
|
||||
|
||||
foreach (var err in errors)
|
||||
foreach (var err in errors.Where(x => x is not null))
|
||||
{
|
||||
if (err.IsEmpty) continue;
|
||||
|
||||
@@ -309,7 +315,7 @@ public sealed class ManyErrors : Error, IEnumerable<Error>, IReadOnlyList<Error>
|
||||
[Pure] public override int Count => _errors.Length;
|
||||
[Pure] public override bool IsEmpty => _errors.IsEmpty;
|
||||
[Pure] public override bool IsExpected => _errors.All(static x => x.IsExpected);
|
||||
[Pure] public override bool IsExeptional => _errors.Any(static x => x.IsExeptional);
|
||||
[Pure] public override bool IsExceptional => _errors.Any(static x => x.IsExceptional);
|
||||
|
||||
[Pure] public Error this[int index] => _errors[index];
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ public sealed class ErrorJsonConverter : JsonConverter<Error>
|
||||
{
|
||||
return reader.TokenType switch
|
||||
{
|
||||
JsonTokenType.StartObject => ToExpectedError(ReadOne(ref reader)),
|
||||
JsonTokenType.StartObject => ToExpectedError(ReadOne(ref reader)), // deserialization always produce ExpectedError by design
|
||||
JsonTokenType.StartArray => ReadMany(ref reader),
|
||||
JsonTokenType.None => null,
|
||||
JsonTokenType.Null => null,
|
||||
@@ -42,6 +42,7 @@ public sealed class ErrorJsonConverter : JsonConverter<Error>
|
||||
{
|
||||
errors.Add(ToExpectedError(ReadOne(ref reader)));
|
||||
}
|
||||
else if (reader.TokenType == JsonTokenType.EndArray) break;
|
||||
}
|
||||
return new ManyErrors(errors.ToImmutable());
|
||||
}
|
||||
@@ -73,11 +74,11 @@ public sealed class ErrorJsonConverter : JsonConverter<Error>
|
||||
if (string.IsNullOrEmpty(propvalue))
|
||||
break;
|
||||
|
||||
if (propname == "type" || string.Equals(propname, "type", StringComparison.InvariantCultureIgnoreCase))
|
||||
if (string.Equals(propname, "type", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
type = propvalue;
|
||||
}
|
||||
else if (propname == "msg" || string.Equals(propname, "msg", StringComparison.InvariantCultureIgnoreCase))
|
||||
else if (string.Equals(propname, "msg", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
message = propvalue;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<Description>Base for railway-oriented programming in .NET. Package includes Result object, Error class and most of the common extensions.</Description>
|
||||
<PackageTags>railway-oriented;functional;result-pattern;result-object;error-handling</PackageTags>
|
||||
<Authors>JustFixMe</Authors>
|
||||
<Copyright>Copyright (c) 2023-2025 JustFixMe</Copyright>
|
||||
<Copyright>Copyright (c) 2023-2026 JustFixMe</Copyright>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/JustFixMe/Just.Railway/</RepositoryUrl>
|
||||
|
||||
+4
-4
@@ -64,7 +64,7 @@ public readonly partial struct Result : IEquatable<Result>
|
||||
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))));
|
||||
Error.New(exception ?? throw new ArgumentNullException(nameof(exception))));
|
||||
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Result<SuccessUnit>(Result result) => result.State switch
|
||||
{
|
||||
@@ -75,8 +75,8 @@ public readonly partial struct Result : IEquatable<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 IsSuccess => State == ResultState.Success;
|
||||
[Pure] public bool IsFailure => State == ResultState.Error;
|
||||
|
||||
[Pure] public bool TryGetValue([MaybeNullWhen(false)]out SuccessUnit? u, [MaybeNullWhen(true), NotNullWhen(false)]out Error? error)
|
||||
{
|
||||
@@ -236,7 +236,7 @@ public readonly struct Result<T> : IEquatable<Result<T>>
|
||||
if (typeof(R).IsAssignableFrom(typeof(T)) && Value is null)
|
||||
return default(R)!;
|
||||
|
||||
return (R)(object)Value!;
|
||||
return (R)(object)Value!; // throws InvalidCastException by design. should not use cast on incompatible types
|
||||
}
|
||||
|
||||
default: throw new ResultNotInitializedException();
|
||||
|
||||
@@ -98,7 +98,7 @@ public static partial class ResultExtensions
|
||||
case ResultState.Success:
|
||||
if (hasErrors) goto afterLoop;
|
||||
break;
|
||||
|
||||
|
||||
default: throw new ResultNotInitializedException(nameof(results));
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,19 @@ public static partial class ResultExtensions
|
||||
}
|
||||
public static async Task<Result<IEnumerable<T>>> Merge<T>(this IEnumerable<Task<Result<T>>> tasks)
|
||||
{
|
||||
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
var taskList = tasks.ToList();
|
||||
var results = new Result<T>[taskList.Count];
|
||||
|
||||
for (int i = 0; i < taskList.Count; i++)
|
||||
{
|
||||
var task = await Task.WhenAny(taskList);
|
||||
taskList.Remove(task);
|
||||
|
||||
results[i] = task.IsCompletedSuccessfully
|
||||
? task.Result
|
||||
: task.Exception!;
|
||||
}
|
||||
|
||||
return results.Merge();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user