-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from MonacsLib/async-extensions-and-sample
Async extensions for Result, some refactoring and cleanup
- Loading branch information
Showing
16 changed files
with
1,025 additions
and
267 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using static Monacs.Core.Result; | ||
|
||
namespace Monacs.Core.Async | ||
{ | ||
public static class Result | ||
{ | ||
/* BindAsync */ | ||
|
||
public static async Task<Result<TOut>> BindAsync<TIn, TOut>(this Result<TIn> result, Func<TIn, Task<Result<TOut>>> func) => | ||
result.IsOk ? await func(result.Value) : Error<TOut>(result.Error); | ||
|
||
public static async Task<Result<TOut>> BindAsync<TIn, TOut>(this Task<Result<TIn>> resultAsync, Func<TIn, Task<Result<TOut>>> func) => | ||
await (await resultAsync).BindAsync(func); | ||
|
||
public static async Task<Result<TOut>> BindAsync<TIn, TOut>(this Task<Result<TIn>> resultAsync, Func<TIn, Result<TOut>> func) => | ||
(await resultAsync).Bind(func); | ||
|
||
/* MapAsync */ | ||
|
||
public static async Task<Result<TOut>> MapAsync<TIn, TOut>(this Result<TIn> result, Func<TIn, Task<TOut>> func) => | ||
(result.IsOk ? Ok(await func(result.Value)) : Error<TOut>(result.Error)); | ||
|
||
public static async Task<Result<TOut>> MapAsync<TIn, TOut>(this Task<Result<TIn>> resultAsync, Func<TIn, Task<TOut>> func) => | ||
await (await resultAsync).MapAsync(func); | ||
|
||
public static async Task<Result<TOut>> MapAsync<TIn, TOut>(this Task<Result<TIn>> resultAsync, Func<TIn, TOut> func) => | ||
(await resultAsync).Map(func); | ||
|
||
/* MatchAsync */ | ||
|
||
public static async Task<TOut> MatchAsync<TIn, TOut>(this Result<TIn> result, Func<TIn, Task<TOut>> ok, Func<ErrorDetails, Task<TOut>> error) => | ||
result.IsOk ? await ok(result.Value) : await error(result.Error); | ||
|
||
public static async Task<TOut> MatchAsync<TIn, TOut>(this Result<TIn> result, Func<TIn, Task<TOut>> ok, Func<ErrorDetails, TOut> error) => | ||
result.IsOk ? await ok(result.Value) : error(result.Error); | ||
|
||
public static async Task<TOut> MatchAsync<TIn, TOut>(this Result<TIn> result, Func<TIn, TOut> ok, Func<ErrorDetails, Task<TOut>> error) => | ||
result.IsOk ? ok(result.Value) : await error(result.Error); | ||
|
||
public static async Task<TOut> MatchAsync<TIn, TOut>(this Task<Result<TIn>> result, Func<TIn, TOut> ok, Func<ErrorDetails, TOut> error) => | ||
(await result).Match(ok, error); | ||
|
||
public static async Task<TOut> MatchAsync<TIn, TOut>(this Task<Result<TIn>> result, Func<TIn, Task<TOut>> ok, Func<ErrorDetails, Task<TOut>> error) => | ||
await (await result).MatchAsync(ok, error); | ||
|
||
public static async Task<TOut> MatchAsync<TIn, TOut>(this Task<Result<TIn>> result, Func<TIn, Task<TOut>> ok, Func<ErrorDetails, TOut> error) => | ||
await (await result).MatchAsync(ok, error); | ||
|
||
public static async Task<TOut> MatchAsync<TIn, TOut>(this Task<Result<TIn>> result, Func<TIn, TOut> ok, Func<ErrorDetails, Task<TOut>> error) => | ||
await (await result).MatchAsync(ok, error); | ||
|
||
/* IgnoreAsync */ | ||
|
||
public static async Task<Result<Monacs.Core.Unit.Unit>> IgnoreAsync<T>(this Task<Result<T>> result) => | ||
(await result).Map(_ => Monacs.Core.Unit.Unit.Default); | ||
|
||
/* Side Effects */ | ||
|
||
public static async Task<Result<T>> DoAsync<T>(this Task<Result<T>> resultAsync, Action<T> action) => | ||
(await resultAsync).Do(action); | ||
|
||
public static async Task<Result<T>> DoAsync<T>(this Result<T> result, Func<T, Task> action) | ||
{ | ||
if (result.IsOk) | ||
await action(result.Value); | ||
return result; | ||
} | ||
|
||
public static async Task<Result<T>> DoAsync<T>(this Task<Result<T>> resultAsync, Func<T, Task> action) => | ||
await (await resultAsync).DoAsync(action); | ||
|
||
public static async Task<Result<T>> DoWhenErrorAsync<T>(this Task<Result<T>> resultAsync, Action<ErrorDetails> action) => | ||
(await resultAsync).DoWhenError(action); | ||
|
||
public static async Task<Result<T>> DoWhenErrorAsync<T>(this Result<T> result, Func<ErrorDetails, Task> action) | ||
{ | ||
if (result.IsError) | ||
await action(result.Error); | ||
return result; | ||
} | ||
|
||
public static async Task<Result<T>> DoWhenErrorAsync<T>(this Task<Result<T>> resultAsync, Func<ErrorDetails, Task> action) => | ||
await (await resultAsync).DoWhenErrorAsync(action); | ||
|
||
/* Flip */ | ||
|
||
public static async Task<Result<T>> FlipAsync<T>(this Result<Task<T>> result) => | ||
result.IsOk | ||
? Ok(await result.Value) | ||
: Error<T>(result.Error); | ||
|
||
/* TryCatchAsync */ | ||
|
||
public static async Task<Result<T>> TryCatchAsync<T>(Func<Task<T>> func, Func<Exception, ErrorDetails> errorHandler) | ||
{ | ||
try | ||
{ | ||
var result = await func(); | ||
return Ok(result); | ||
} | ||
catch (Exception ex) | ||
{ | ||
return Error<T>(errorHandler(ex)); | ||
} | ||
} | ||
|
||
public static async Task<Result<TOut>> TryCatchAsync<TIn, TOut>(this Result<TIn> result, Func<TIn, Task<TOut>> func, Func<TIn, Exception, ErrorDetails> errorHandler) => | ||
await result.BindAsync(value => TryCatchAsync(() => func(value), e => errorHandler(value, e))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System; | ||
using static Monacs.Core.Option; | ||
|
||
namespace Monacs.Core | ||
{ | ||
public static class Errors | ||
{ | ||
public static ErrorDetails Trace(string message = null, string key = null, Exception exception = null) => | ||
new ErrorDetails(ErrorLevel.Trace, message.ToOption(), key.ToOption(), exception.ToOption()); | ||
|
||
public static ErrorDetails Debug(string message = null, string key = null, Exception exception = null) => | ||
new ErrorDetails(ErrorLevel.Debug, message.ToOption(), key.ToOption(), exception.ToOption()); | ||
|
||
public static ErrorDetails Info(string message = null, string key = null, Exception exception = null) => | ||
new ErrorDetails(ErrorLevel.Info, message.ToOption(), key.ToOption(), exception.ToOption()); | ||
|
||
public static ErrorDetails Warn(string message = null, string key = null, Exception exception = null) => | ||
new ErrorDetails(ErrorLevel.Warn, message.ToOption(), key.ToOption(), exception.ToOption()); | ||
|
||
public static ErrorDetails Error(string message = null, string key = null, Exception exception = null) => | ||
new ErrorDetails(ErrorLevel.Error, message.ToOption(), key.ToOption(), exception.ToOption()); | ||
|
||
public static ErrorDetails Fatal(string message = null, string key = null, Exception exception = null) => | ||
new ErrorDetails(ErrorLevel.Fatal, message.ToOption(), key.ToOption(), exception.ToOption()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace Monacs.Core | ||
{ | ||
public static class Option | ||
{ | ||
/* Constructors */ | ||
|
||
public static Option<T> Some<T>(T value) => new Option<T>(value); | ||
|
||
public static Option<T> None<T>() => default(Option<T>); | ||
|
||
/* Converters */ | ||
|
||
public static Option<T> OfObject<T>(T value) where T : class => | ||
value != null ? Some(value) : None<T>(); | ||
|
||
public static Option<T> ToOption<T>(this T value) where T : class => OfObject(value); | ||
|
||
public static Option<T> OfNullable<T>(T? value) where T : struct => | ||
value.HasValue ? Some(value.Value) : None<T>(); | ||
|
||
public static Option<T> ToOption<T>(this T? value) where T : struct => OfNullable(value); | ||
|
||
public static T? ToNullable<T>(Option<T> value) where T : struct => | ||
value.IsSome ? value.Value : (T?)null; | ||
|
||
public static Option<string> OfString(string value) => | ||
string.IsNullOrEmpty(value) ? None<string>() : Some(value); | ||
|
||
public static Option<string> ToOption(this string value) => OfString(value); | ||
|
||
public static Option<T> OfResult<T>(Result<T> value) where T : struct => | ||
value.IsOk ? Some(value.Value) : None<T>(); | ||
|
||
public static Option<T> ToOption<T>(this Result<T> value) where T : struct => OfResult(value); | ||
|
||
/* TryGetOption */ | ||
|
||
public static Option<TValue> TryGetOption<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key) => | ||
dict.TryGetValue(key, out TValue value) ? Some(value) : None<TValue>(); | ||
|
||
public static Option<IEnumerable<TValue>> TryGetOption<TKey, TValue>(this ILookup<TKey, TValue> lookup, TKey key) => | ||
lookup.Contains(key) ? Some(lookup[key]) : None<IEnumerable<TValue>>(); | ||
|
||
/* Match */ | ||
|
||
public static T2 Match<T1, T2>(this Option<T1> option, Func<T1, T2> some, Func<T2> none) => | ||
option.IsSome ? some(option.Value) : none(); | ||
|
||
public static T2 MatchTo<T1, T2>(this Option<T1> option, T2 some, T2 none) => | ||
option.IsSome ? some : none; | ||
|
||
/* Bind */ | ||
|
||
public static Option<T2> Bind<T1, T2>(this Option<T1> option, Func<T1, Option<T2>> binder) => | ||
option.IsSome ? binder(option.Value) : None<T2>(); | ||
|
||
/* Map */ | ||
|
||
public static Option<T2> Map<T1, T2>(this Option<T1> option, Func<T1, T2> mapper) => | ||
option.IsSome ? Some(mapper(option.Value)) : None<T2>(); | ||
|
||
/* Getters */ | ||
|
||
public static T GetOrDefault<T>(this Option<T> option, T whenNone = default(T)) => | ||
option.IsSome ? option.Value : whenNone; | ||
|
||
public static T2 GetOrDefault<T1, T2>(this Option<T1> option, Func<T1, T2> getter, T2 whenNone = default(T2)) => | ||
option.IsSome ? getter(option.Value) : whenNone; | ||
|
||
/* Side Effects */ | ||
|
||
public static Option<T> Do<T>(this Option<T> option, Action<T> action) | ||
{ | ||
if (option.IsSome) | ||
action(option.Value); | ||
return option; | ||
} | ||
|
||
public static Option<T> DoWhenNone<T>(this Option<T> option, Action action) | ||
{ | ||
if (option.IsNone) | ||
action(); | ||
return option; | ||
} | ||
|
||
/* Collections */ | ||
|
||
public static IEnumerable<T> Choose<T>(this IEnumerable<Option<T>> items) => | ||
items.Where(i => i.IsSome).Select(i => i.Value); | ||
|
||
public static Option<IEnumerable<T>> Sequence<T>(this IEnumerable<Option<T>> items) => | ||
items.Any(i => i.IsNone) | ||
? None<IEnumerable<T>>() | ||
: Some(items.Select(i => i.Value)); | ||
|
||
public static Option<T> TryFind<T>(this IEnumerable<T> items, Func<T, bool> predicate) | ||
{ | ||
if (items != null) | ||
foreach (T element in items) { | ||
if (predicate(element)) return Some(element); | ||
} | ||
return None<T>(); | ||
} | ||
|
||
public static Option<T> TryFirst<T>(this IEnumerable<T> items) | ||
{ | ||
if (items == null) return None<T>(); | ||
IList<T> list = items as IList<T>; | ||
if (list != null) { | ||
if (list.Count > 0) return Some(list[0]); | ||
} else { | ||
foreach (T element in items) { | ||
return Some(element); | ||
} | ||
} | ||
return None<T>(); | ||
} | ||
|
||
public static Option<T> TryElementAt<T>(this IEnumerable<T> items, int index) | ||
{ | ||
if (items == null || index < 0) return None<T>(); | ||
IList<T> list = items as IList<T>; | ||
if (list != null) { | ||
if (list.Count >= index+1) return Some(list[index]); | ||
} else { | ||
int idx = 0; | ||
foreach (T element in items) { | ||
if (idx == index) return Some(element); | ||
idx++; | ||
} | ||
} | ||
return None<T>(); | ||
} | ||
} | ||
} |
Oops, something went wrong.