Skip to content

Commit e1ee1f1

Browse files
committed
Adjustments for AwaitableResult
1 parent d0011cd commit e1ee1f1

File tree

3 files changed

+201
-76
lines changed

3 files changed

+201
-76
lines changed

src/DotNext/Optional.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static async Task<Optional<TOutput>> Convert<TInput, TOutput>(this Task<O
7878
/// <typeparam name="T">The type of the value.</typeparam>
7979
/// <param name="value">The value to be placed to the container.</param>
8080
/// <returns>The value encapsulated by <see cref="Result{T}"/>.</returns>
81-
public static Optional<T> FromValue<T>(T value) => new(value);
81+
public static Optional<T> From<T>(T value) => new(value);
8282

8383
/// <summary>
8484
/// Creates <see cref="Result{T}"/> from <see cref="Optional{T}"/> instance.

src/DotNext/Result.cs

Lines changed: 150 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
namespace DotNext;
99

10+
using System;
11+
using DotNext.Threading.Tasks;
1012
using Runtime.CompilerServices;
1113
using Intrinsics = Runtime.Intrinsics;
1214

@@ -68,17 +70,6 @@ public static class Result
6870
/// <returns>The value encapsulated by <see cref="Result{T}"/>.</returns>
6971
public static Result<T> FromValue<T>(T value) => new(value);
7072

71-
/// <summary>
72-
/// Creates a new instance of <see cref="Result{T}"/> from the specified value.
73-
/// </summary>
74-
/// <typeparam name="T">The type of the value.</typeparam>
75-
/// <typeparam name="TError">The type of the error code. Default value must represent the successful result.</typeparam>
76-
/// <param name="value">The value to be placed to the container.</param>
77-
/// <returns>The value encapsulated by <see cref="Result{T, TError}"/>.</returns>
78-
public static Result<T, TError> FromValue<T, TError>(T value)
79-
where TError: struct, Enum
80-
=> new(value);
81-
8273
/// <summary>
8374
/// Creates a new instance of <see cref="Result{T}"/> from the specified exception.
8475
/// </summary>
@@ -98,6 +89,30 @@ public static Result<T, TError> FromError<T, TError>(TError e)
9889
where TError: struct, Enum
9990
=> new(e);
10091

92+
private static AwaitableResult<TResult> TransformAwaitableResult<T, TResult>(this AwaitableResult<T> task, Converter<Result<T>, Result<TResult>> converter)
93+
{
94+
async Task<TResult> ConvertInternal()
95+
{
96+
var result = await task.ConfigureAwait(false);
97+
var conversionResult = converter(result);
98+
return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error;
99+
}
100+
101+
return ConvertInternal().SuspendException();
102+
}
103+
104+
private static AwaitableResult<TResult> TransformAwaitableResult<T, TResult>(this AwaitableResult<T> task, Converter<Result<T>, AwaitableResult<TResult>> converter)
105+
{
106+
async Task<TResult> ConvertInternal()
107+
{
108+
var result = await task.ConfigureAwait(false);
109+
var conversionResult = await converter(result);
110+
return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error;
111+
}
112+
113+
return ConvertInternal().SuspendException();
114+
}
115+
101116
/// <summary>
102117
/// If successful result is present, apply the provided mapping function hiding any exception
103118
/// caused by the converter.
@@ -107,8 +122,8 @@ public static Result<T, TError> FromError<T, TError>(TError e)
107122
/// <typeparam name="T">The type of the value.</typeparam>
108123
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
109124
/// <returns>The conversion result.</returns>
110-
public static async Task<Result<TResult>> Convert<T, TResult>(this Task<Result<T>> task, Converter<T, TResult> converter)
111-
=> (await task.ConfigureAwait(false)).Convert(converter);
125+
public static AwaitableResult<TResult> Convert<T, TResult>(this AwaitableResult<T> task, Converter<T, TResult> converter)
126+
=> task.TransformAwaitableResult((result) => result.Convert(converter));
112127

113128
/// <summary>
114129
/// If successful result is present, apply the provided mapping function. If not,
@@ -119,8 +134,8 @@ public static async Task<Result<TResult>> Convert<T, TResult>(this Task<Result<T
119134
/// <typeparam name="T">The type of the value.</typeparam>
120135
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
121136
/// <returns>The conversion result.</returns>
122-
public static async Task<Result<TResult>> Convert<T, TResult>(this Task<Result<T>> task, Converter<T, Result<TResult>> converter)
123-
=> (await task.ConfigureAwait(false)).Convert(converter);
137+
public static AwaitableResult<TResult> Convert<T, TResult>(this AwaitableResult<T> task, Converter<T, Result<TResult>> converter)
138+
=> task.TransformAwaitableResult((result) => result.Convert(converter));
124139

125140
/// <summary>
126141
/// If successful result is present, apply the provided mapping function. If not,
@@ -131,50 +146,32 @@ public static async Task<Result<TResult>> Convert<T, TResult>(this Task<Result<T
131146
/// <typeparam name="T">The type of the value.</typeparam>
132147
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
133148
/// <returns>The conversion result.</returns>
134-
public static async Task<Result<TResult>> Convert<T, TResult>(this Task<Result<T>> task, Converter<T, Task<Result<TResult>>> converter)
135-
=> await (await task.ConfigureAwait(false)).Convert(converter).ConfigureAwait(false);
136-
137-
/// <summary>
138-
/// If successful result is present, apply the provided mapping function hiding any exception
139-
/// caused by the converter.
140-
/// </summary>
141-
/// <param name="task">The task containing Result value.</param>
142-
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
143-
/// <typeparam name="T">The type of the value.</typeparam>
144-
/// <typeparam name="TError">The type of the error code. Default value must represent the successful result.</typeparam>
145-
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
146-
/// <returns>The conversion result.</returns>
147-
public static async Task<Result<TResult, TError>> Convert<T, TError, TResult>(this Task<Result<T, TError>> task, Converter<T, TResult> converter)
148-
where TError: struct, Enum
149-
=> (await task.ConfigureAwait(false)).Convert(converter);
149+
public static AwaitableResult<TResult> Convert<T, TResult>(this AwaitableResult<T> task, Converter<T, Task<TResult>> converter)
150+
=> task.TransformAwaitableResult((result) => result.Convert(converter));
150151

151152
/// <summary>
152153
/// If successful result is present, apply the provided mapping function. If not,
153-
/// forward the error.
154+
/// forward the exception.
154155
/// </summary>
155156
/// <param name="task">The task containing Result value.</param>
156157
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
157158
/// <typeparam name="T">The type of the value.</typeparam>
158-
/// <typeparam name="TError">The type of the error code. Default value must represent the successful result.</typeparam>
159159
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
160160
/// <returns>The conversion result.</returns>
161-
public static async Task<Result<TResult, TError>> Convert<T, TError, TResult>(this Task<Result<T, TError>> task, Converter<T, Result<TResult, TError>> converter)
162-
where TError : struct, Enum
163-
=> (await task.ConfigureAwait(false)).Convert(converter);
161+
public static AwaitableResult<TResult> Convert<T, TResult>(this AwaitableResult<T> task, Converter<T, Task<Result<TResult>>> converter)
162+
=> task.TransformAwaitableResult((result) => result.Convert(converter));
164163

165164
/// <summary>
166165
/// If successful result is present, apply the provided mapping function. If not,
167-
/// forward the error.
166+
/// forward the exception.
168167
/// </summary>
169168
/// <param name="task">The task containing Result value.</param>
170169
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
171170
/// <typeparam name="T">The type of the value.</typeparam>
172-
/// <typeparam name="TError">The type of the error code. Default value must represent the successful result.</typeparam>
173171
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
174172
/// <returns>The conversion result.</returns>
175-
public static async Task<Result<TResult, TError>> Convert<T, TError, TResult>(this Task<Result<T, TError>> task, Converter<T, Task<Result<TResult, TError>>> converter)
176-
where TError : struct, Enum
177-
=> await (await task.ConfigureAwait(false)).Convert(converter).ConfigureAwait(false);
173+
public static AwaitableResult<TResult> Convert<T, TResult>(this AwaitableResult<T> task, Converter<T, AwaitableResult<TResult>> converter)
174+
=> task.TransformAwaitableResult((result) => result.Convert(converter));
178175

179176
/// <summary>
180177
/// Converts the result into <see cref="Optional{T}"/>.
@@ -183,6 +180,13 @@ public static async Task<Result<TResult, TError>> Convert<T, TError, TResult>(th
183180
public static async Task<Optional<T>> TryGet<T>(this Task<Result<T>> task)
184181
=> (await task.ConfigureAwait(false)).TryGet();
185182

183+
/// <summary>
184+
/// Converts the awaitable Result into a task holding <see cref="Optional{T}"/>.
185+
/// </summary>
186+
/// <returns>A task holding an Option monad representing value in this monad.</returns>
187+
public static async Task<Optional<T>> TryGet<T>(this AwaitableResult<T> awaitableResult)
188+
=> (await awaitableResult.ConfigureAwait(false)).TryGet();
189+
186190
/// <summary>
187191
/// Converts the result into <see cref="Optional{T}"/>.
188192
/// </summary>
@@ -341,24 +345,72 @@ private Result<TResult> ConvertResult<TResult, TConverter>(TConverter converter)
341345
}
342346

343347
[MethodImpl(MethodImplOptions.AggressiveInlining)]
344-
private Task<Result<TResult>> ConvertResultTask<TResult, TConverter>(TConverter converter)
345-
where TConverter : struct, ISupplier<T, Task<Result<TResult>>>
348+
private AwaitableResult<TResult> ConvertTask<TResult, TConverter>(TConverter converter)
349+
where TConverter : struct, ISupplier<T, Task<TResult>>
346350
{
347-
Task<Result<TResult>> result;
351+
AwaitableResult<TResult> result;
348352
if (exception is null)
349353
{
350354
try
351355
{
352-
result = converter.Invoke(value);
356+
result = converter.Invoke(value).SuspendException();
353357
}
354358
catch (Exception e)
355359
{
356-
result = Task.FromResult(new Result<TResult>(e));
360+
result = new(Task.FromException<TResult>(e));
357361
}
358362
}
359363
else
360364
{
361-
result = Task.FromResult(new Result<TResult>(exception));
365+
result = new(Task.FromException<TResult>(exception.SourceException));
366+
}
367+
368+
return result;
369+
}
370+
371+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
372+
private AwaitableResult<TResult> ConvertResultTask<TResult, TConverter>(TConverter converter)
373+
where TConverter : struct, ISupplier<T, Task<Result<TResult>>>
374+
{
375+
AwaitableResult<TResult> result;
376+
if (exception is null)
377+
{
378+
var valueCopy = value;
379+
async Task<TResult> GetConversionResult()
380+
{
381+
var conversionResult = await converter.Invoke(valueCopy).ConfigureAwait(false);
382+
return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error;
383+
}
384+
385+
result = new(GetConversionResult());
386+
}
387+
else
388+
{
389+
result = new(Task.FromException<TResult>(exception.SourceException));
390+
}
391+
392+
return result;
393+
}
394+
395+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
396+
private AwaitableResult<TResult> ConvertAwaitableResult<TResult, TConverter>(TConverter converter)
397+
where TConverter : struct, ISupplier<T, AwaitableResult<TResult>>
398+
{
399+
AwaitableResult<TResult> result;
400+
if (exception is null)
401+
{
402+
var valueCopy = value;
403+
async Task<TResult> GetConversionResult()
404+
{
405+
var conversionResult = await converter.Invoke(valueCopy).ConfigureAwait(false);
406+
return conversionResult.IsSuccessful ? conversionResult.Value : throw conversionResult.Error;
407+
}
408+
409+
result = new(GetConversionResult());
410+
}
411+
else
412+
{
413+
result = new(Task.FromException<TResult>(exception.SourceException));
362414
}
363415

364416
return result;
@@ -391,9 +443,29 @@ public Result<TResult> Convert<TResult>(Converter<T, Result<TResult>> converter)
391443
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
392444
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
393445
/// <returns>The conversion result.</returns>
394-
public Task<Result<TResult>> Convert<TResult>(Converter<T, Task<Result<TResult>>> converter)
446+
public AwaitableResult<TResult> Convert<TResult>(Converter<T, Task<TResult>> converter)
447+
=> ConvertTask<TResult, DelegatingConverter<T, Task<TResult>>>(converter);
448+
449+
/// <summary>
450+
/// If successful result is present, apply the provided mapping function. If not,
451+
/// forward the exception.
452+
/// </summary>
453+
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
454+
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
455+
/// <returns>The conversion result.</returns>
456+
public AwaitableResult<TResult> Convert<TResult>(Converter<T, Task<Result<TResult>>> converter)
395457
=> ConvertResultTask<TResult, DelegatingConverter<T, Task<Result<TResult>>>>(converter);
396458

459+
/// <summary>
460+
/// If successful result is present, apply the provided mapping function. If not,
461+
/// forward the exception.
462+
/// </summary>
463+
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
464+
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
465+
/// <returns>The conversion result.</returns>
466+
public AwaitableResult<TResult> Convert<TResult>(Converter<T, AwaitableResult<TResult>> converter)
467+
=> ConvertAwaitableResult<TResult, DelegatingConverter<T, AwaitableResult<TResult>>>(converter);
468+
397469
/// <summary>
398470
/// If successful result is present, apply the provided mapping function hiding any exception
399471
/// caused by the converter.
@@ -424,9 +496,31 @@ public unsafe Result<TResult> Convert<TResult>(delegate*<T, Result<TResult>> con
424496
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
425497
/// <returns>The conversion result.</returns>
426498
[CLSCompliant(false)]
427-
public unsafe Task<Result<TResult>> Convert<TResult>(delegate*<T, Task<Result<TResult>>> converter)
499+
public unsafe AwaitableResult<TResult> Convert<TResult>(delegate*<T, Task<TResult>> converter)
500+
=> ConvertTask<TResult, Supplier<T, Task<TResult>>>(converter);
501+
502+
/// <summary>
503+
/// If successful result is present, apply the provided mapping function. If not,
504+
/// forward the exception.
505+
/// </summary>
506+
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
507+
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
508+
/// <returns>The conversion result.</returns>
509+
[CLSCompliant(false)]
510+
public unsafe AwaitableResult<TResult> Convert<TResult>(delegate*<T, Task<Result<TResult>>> converter)
428511
=> ConvertResultTask<TResult, Supplier<T, Task<Result<TResult>>>>(converter);
429512

513+
/// <summary>
514+
/// If successful result is present, apply the provided mapping function. If not,
515+
/// forward the exception.
516+
/// </summary>
517+
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
518+
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
519+
/// <returns>The conversion result.</returns>
520+
[CLSCompliant(false)]
521+
public unsafe AwaitableResult<TResult> Convert<TResult>(delegate*<T, AwaitableResult<TResult>> converter)
522+
=> ConvertAwaitableResult<TResult, Supplier<T, AwaitableResult<TResult>>>(converter);
523+
430524
/// <summary>
431525
/// Attempts to extract value from container if it is present.
432526
/// </summary>
@@ -527,6 +621,13 @@ public ValueTask<T> AsTask()
527621
{ } error => ValueTask.FromException<T>(error),
528622
};
529623

624+
/// <summary>
625+
/// Converts this result to <see cref="AwaitableResult{TResult}"/>.
626+
/// </summary>
627+
/// <returns>The completed task representing the result.</returns>
628+
public AwaitableResult<T> ToAwaitable()
629+
=> IsSuccessful ? new(Task.FromResult(value)) : new(Task.FromException<T>(Error));
630+
530631
/// <summary>
531632
/// Converts the result to <see cref="Task{TResult}"/>.
532633
/// </summary>
@@ -747,11 +848,6 @@ private Result<TResult, TError> ConvertResult<TResult, TConverter>(TConverter co
747848
where TConverter : struct, ISupplier<T, Result<TResult, TError>>
748849
=> IsSuccessful ? converter.Invoke(value) : new(Error);
749850

750-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
751-
private Task<Result<TResult, TError>> ConvertResultTask<TResult, TConverter>(TConverter converter)
752-
where TConverter : struct, ISupplier<T, Task<Result<TResult, TError>>>
753-
=> IsSuccessful ? converter.Invoke(value) : Task.FromResult(new Result<TResult, TError>(Error));
754-
755851
/// <summary>
756852
/// If successful result is present, apply the provided mapping function hiding any exception
757853
/// caused by the converter.
@@ -772,16 +868,6 @@ public Result<TResult, TError> Convert<TResult>(Converter<T, TResult> converter)
772868
public Result<TResult, TError> Convert<TResult>(Converter<T, Result<TResult, TError>> converter)
773869
=> ConvertResult<TResult, DelegatingConverter<T, Result<TResult,TError>>>(converter);
774870

775-
/// <summary>
776-
/// If successful result is present, apply the provided mapping function. If not,
777-
/// forward the error.
778-
/// </summary>
779-
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
780-
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
781-
/// <returns>The conversion result.</returns>
782-
public Task<Result<TResult, TError>> Convert<TResult>(Converter<T, Task<Result<TResult, TError>>> converter)
783-
=> ConvertResultTask<TResult, DelegatingConverter<T, Task<Result<TResult, TError>>>>(converter);
784-
785871
/// <summary>
786872
/// If successful result is present, apply the provided mapping function hiding any exception
787873
/// caused by the converter.
@@ -804,17 +890,6 @@ public unsafe Result<TResult, TError> Convert<TResult>(delegate*<T, TResult> con
804890
public unsafe Result<TResult, TError> Convert<TResult>(delegate*<T, Result<TResult, TError>> converter)
805891
=> ConvertResult<TResult, Supplier<T, Result<TResult, TError>>>(converter);
806892

807-
/// <summary>
808-
/// If successful result is present, apply the provided mapping function. If not,
809-
/// forward the error.
810-
/// </summary>
811-
/// <param name="converter">A mapping function to be applied to the value, if present.</param>
812-
/// <typeparam name="TResult">The type of the result of the mapping function.</typeparam>
813-
/// <returns>The conversion result.</returns>
814-
[CLSCompliant(false)]
815-
public unsafe Task<Result<TResult, TError>> Convert<TResult>(delegate*<T, Task<Result<TResult, TError>>> converter)
816-
=> ConvertResultTask<TResult, Supplier<T, Task<Result<TResult, TError>>>>(converter);
817-
818893
[MethodImpl(MethodImplOptions.AggressiveInlining)]
819894
private T OrInvoke<TSupplier>(TSupplier defaultFunc)
820895
where TSupplier : struct, ISupplier<T>

0 commit comments

Comments
 (0)