Skip to content

Commit

Permalink
Merge pull request #5 from MonacsLib/async-extensions-and-sample
Browse files Browse the repository at this point in the history
Async extensions for Result, some refactoring and cleanup
  • Loading branch information
bartsokol authored Feb 2, 2018
2 parents c237240 + 1eb4383 commit 0c7e6f2
Show file tree
Hide file tree
Showing 16 changed files with 1,025 additions and 267 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: csharp
mono: latest
mono: alpha
dotnet: 2.0.0
dist: trusty
env: FrameworkPathOverride=/usr/lib/mono/4.5/
Expand All @@ -12,4 +12,4 @@ deploy:
script:
- cd Monacs.Core && dotnet pack Monacs.Core.csproj -c Release -o . -v n && dotnet nuget push Monacs.Core.*.nupkg -k $NUGET_API_KEY -s https://www.nuget.org/api/v2/package
on:
branch: master
branch: master
114 changes: 114 additions & 0 deletions Monacs.Core/Async/Result.Async.Extensions.cs
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)));
}
}
22 changes: 1 addition & 21 deletions Monacs.Core/ErrorDetails.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using static Monacs.Core.Option;

namespace Monacs.Core
{
Expand Down Expand Up @@ -30,25 +31,4 @@ public enum ErrorLevel
Error = 4,
Fatal = 5
}

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());
}
}
26 changes: 26 additions & 0 deletions Monacs.Core/Errors.cs
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());
}
}
1 change: 1 addition & 0 deletions Monacs.Core/Monacs.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard1.3;net461</TargetFrameworks>
<NuspecFile>$(MSBuildThisFileDirectory)$(MSBuildProjectName).nuspec</NuspecFile>
<LangVersion>latest</LangVersion>
</PropertyGroup>

</Project>
2 changes: 1 addition & 1 deletion Monacs.Core/Monacs.Core.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Monacs.Core</id>
<version>0.0.7-alpha</version>
<version>0.1.0</version>
<authors>Bartosz Sokół</authors>
<owners>Bartosz Sokół</owners>
<licenseUrl>https://github.com/bartsokol/Monacs/blob/master/LICENSE</licenseUrl>
Expand Down
139 changes: 139 additions & 0 deletions Monacs.Core/Option.Extensions.cs
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>();
}
}
}
Loading

0 comments on commit 0c7e6f2

Please sign in to comment.