Skip to content

Commit

Permalink
Railway-Oriented Extensions (#2)
Browse files Browse the repository at this point in the history
* Added Railway-oriented extension methods (Ensure, EnsureNotNull, Then,
Tap, And)
* Fixed .editorconfig
  • Loading branch information
skrasekmichael authored Mar 16, 2024
1 parent 8f42ff5 commit b9a83e6
Show file tree
Hide file tree
Showing 11 changed files with 560 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent

# Code-block preferences
csharp_prefer_braces = true:warning
csharp_prefer_braces = when_multiline:error
csharp_prefer_simple_using_statement = true:suggestion

# Expression-level preferences
Expand Down
6 changes: 6 additions & 0 deletions RailwayResult.FunctionExtensions/IRuleWithError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace RailwayResult.FunctionExtensions;

public interface IRuleWithError<T>
{
public Result<T> Apply(T val);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\src\RailwayResult.Errors\RailwayResult.Errors.csproj" />
<ProjectReference Include="..\src\RailwayResult\RailwayResult.csproj" />
</ItemGroup>

</Project>
86 changes: 86 additions & 0 deletions RailwayResult.FunctionExtensions/ResultExtensions.And.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
namespace RailwayResult.FunctionExtensions;

public static partial class ResultExtensions
{
public static Result<(TFirst, TSecond)> And<TFirst, TSecond>(this Result<TFirst> result, Func<TSecond> func)
{
if (result.IsFailure)
return result.Error;

return (result.Value, func());
}

public static async Task<Result<(TFirst, TSecond)>> And<TFirst, TSecond>(this Task<Result<TFirst>> resultTask, Func<TSecond> func)
{
var result = await resultTask;
return result.And(func);
}

public static Result<(TFirst, TSecond)> And<TFirst, TSecond>(this Result<TFirst> result, Func<Result<TSecond>> func)
{
if (result.IsFailure)
return result.Error;

var nestedResult = func();
if (nestedResult.IsFailure)
return nestedResult.Error;

return (result.Value, nestedResult.Value);
}

public static async Task<Result<(TFirst, TSecond)>> And<TFirst, TSecond>(this Task<Result<TFirst>> resultTask, Func<Result<TSecond>> func)
{
var result = await resultTask;
return result.And(func);
}

public static Result<(TFirst, TSecond)> And<TFirst, TSecond>(this Result<TFirst> result, Func<TFirst, Result<TSecond>> func)
{
if (result.IsFailure)
return result.Error;

var nestedResult = func(result.Value);
if (nestedResult.IsFailure)
return nestedResult.Error;

return (result.Value, nestedResult.Value);
}

public static async Task<Result<(TFirst, TSecond)>> And<TFirst, TSecond>(this Task<Result<TFirst>> resultTask, Func<TFirst, Result<TSecond>> func)
{
var result = await resultTask;
return result.And(func);
}

public static Result<(TFirst, TSecond, TThird)> And<TFirst, TSecond, TThird>(this Result<(TFirst, TSecond)> result, Func<TFirst, TSecond, Result<TThird>> func)
{
if (result.IsFailure)
return result.Error;

var nestedResult = func(result.Value.Item1, result.Value.Item2);
if (nestedResult.IsFailure)
return nestedResult.Error;

return (result.Value.Item1, result.Value.Item2, nestedResult.Value);
}

public static async Task<Result<(TFirst, TSecond, TThird)>> And<TFirst, TSecond, TThird>(this Task<Result<(TFirst, TSecond)>> resultTask, Func<TFirst, TSecond, Result<TThird>> func)
{
var result = await resultTask;
return result.And(func);
}

public static async Task<Result<(TFirst, TSecond)>> AndAsync<TFirst, TSecond>(this Result<TFirst> result, Func<TFirst, Task<TSecond>> asyncFunc)
{
if (result.IsFailure)
return result.Error;

return (result.Value, await asyncFunc(result.Value));
}

public static async Task<Result<(TFirst, TSecond)>> AndAsync<TFirst, TSecond>(this Task<Result<TFirst>> resultTask, Func<TFirst, Task<TSecond>> asyncFunc)
{
var result = await resultTask;
return await result.AndAsync(asyncFunc);
}
}
105 changes: 105 additions & 0 deletions RailwayResult.FunctionExtensions/ResultExtensions.Ensure.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
namespace RailwayResult.FunctionExtensions;

public static partial class ResultExtensions
{
public static Result<TObj> Ensure<TObj, TError>(this TObj obj, Rule<TObj> rule, TError error) where TError : ErrorBase
{
if (!rule(obj))
return error;

return obj;
}

public static async Task<Result<TObj>> Ensure<TObj, TError>(this Task<TObj> objTask, Rule<TObj> rule, TError error) where TError : ErrorBase
{
var obj = await objTask;
return obj.Ensure(rule, error);
}

public static Result<TObj> Ensure<TObj, TRule>(this TObj obj, params TRule[] rules) where TRule : IRuleWithError<TObj>
{
foreach (var rule in rules)
{
var ruleResult = rule.Apply(obj);
if (ruleResult.IsFailure)
return ruleResult;
}

return obj;
}

public static async Task<Result<TObj>> Ensure<TObj, TRule>(this Task<TObj> objTask, params TRule[] rules) where TRule : IRuleWithError<TObj>
{
var obj = await objTask;
return obj.Ensure(rules);
}

public static Result<TValue> Ensure<TValue, TError>(this Result<TValue> result, Rule<TValue> rule, TError error) where TError : ErrorBase
{
if (result.IsFailure)
return result;

if (!rule(result.Value))
return error;

return result;
}

public static async Task<Result<TValue>> Ensure<TValue, TError>(this Task<Result<TValue>> selfTask, Rule<TValue> rule, TError error) where TError : ErrorBase
{
var self = await selfTask;
return self.Ensure(rule, error);
}

public static Result<TValue> Ensure<TValue, TRule>(this Result<TValue> result, params TRule[] rules) where TRule : IRuleWithError<TValue>
{
if (result.IsFailure)
return result;

foreach (var rule in rules)
{
var ruleResult = rule.Apply(result.Value);
if (ruleResult.IsFailure)
return ruleResult;
}

return result;
}

public static async Task<Result<TValue>> Ensure<TValue, TRule>(this Task<Result<TValue>> resultTask, params TRule[] rules) where TRule : IRuleWithError<TValue>
{
var result = await resultTask;
return result.Ensure(rules);
}

public static Result<(TFirst, TSecond)> Ensure<TFirst, TSecond, TError>(this Result<(TFirst, TSecond)> result, Rule<TFirst, TSecond> rule, TError error) where TError : ErrorBase
{
if (result.IsFailure)
return result;

if (!rule(result.Value.Item1, result.Value.Item2))
return error;

return result;
}

public static async Task<Result<(TFirst, TSecond)>> Ensure<TFirst, TSecond, TError>(this Task<Result<(TFirst, TSecond)>> resultTask, Rule<TFirst, TSecond> rule, TError error) where TError : ErrorBase
{
var result = await resultTask;
return result.Ensure(rule, error);
}

public static Result<(TFirst, TSecond)> Ensure<TFirst, TSecond, TRule>(this Result<(TFirst, TSecond)> result, TRule rule) where TRule : IRuleWithError<(TFirst, TSecond)>
{
if (result.IsFailure)
return result;

return rule.Apply(result.Value);
}

public static async Task<Result<(TFirst, TSecond)>> Ensure<TFirst, TSecond, TRule>(this Task<Result<(TFirst, TSecond)>> resultTask, TRule rule) where TRule : IRuleWithError<(TFirst, TSecond)>
{
var result = await resultTask;
return result.Ensure(rule);
}
}
86 changes: 86 additions & 0 deletions RailwayResult.FunctionExtensions/ResultExtensions.EnsureNotNull.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
namespace RailwayResult.FunctionExtensions;

public static partial class ResultExtensions
{
public static Result<TObj> EnsureNotNull<TObj, TError>(this TObj? obj, TError error) where TError : ErrorBase
{
if (obj is null)
return error;

return obj;
}

public static async Task<Result<TObj>> EnsureNotNull<TObj, TError>(this Task<TObj?> objTask, TError error) where TError : ErrorBase
{
var obj = await objTask;
return obj.EnsureNotNull(error);
}

public static Result<TValue> EnsureNotNull<TValue, TError>(this Result<TValue?> result, TError error) where TError : ErrorBase
{
if (result.IsFailure)
return result.Error;

if (result.Value is null)
return error;

return result.Value;
}

public static async Task<Result<TValue>> EnsureNotNull<TValue, TError>(this Task<Result<TValue?>> resultTask, TError error) where TError : ErrorBase
{
var result = await resultTask;
return result.EnsureNotNull(error);
}

public static Result<TValue> EnsureNotNull<TValue, TProperty, TError>(this Result<TValue> result, Func<TValue, TProperty?> selector, TError error) where TError : ErrorBase
{
if (result.IsFailure)
return result.Error;

if (selector(result.Value) is null)
return error;

return result.Value;
}

public static async Task<Result<TValue>> EnsureNotNull<TValue, TProperty, TError>(this Task<Result<TValue>> resultTask, Func<TValue, TProperty?> selector, TError error) where TError : ErrorBase
{
var result = await resultTask;
return result.EnsureNotNull(selector, error);
}

public static Result<(TFirst, TSecond)> EnsureNotNull<TFirst, TSecond, TProperty, TError>(this Result<(TFirst, TSecond)> result, Func<TFirst, TSecond, TProperty?> selector, TError error) where TError : ErrorBase
{
if (result.IsFailure)
return result.Error;

if (selector(result.Value.Item1, result.Value.Item2) is null)
return error;

return result.Value;
}

public static async Task<Result<(TFirst, TSecond)>> EnsureNotNull<TFirst, TSecond, TProperty, TError>(this Task<Result<(TFirst, TSecond)>> resultTask, Func<TFirst, TSecond, TProperty?> selector, TError error) where TError : ErrorBase
{
var result = await resultTask;
return result.EnsureNotNull(selector, error);
}

public static Result<(TFirst, TSecond)> EnsureSecondNotNull<TFirst, TSecond, TError>(this Result<(TFirst, TSecond?)> result, TError error) where TError : ErrorBase
{
if (result.IsFailure)
return result.Error;

if (result.Value.Item2 is null)
return error;

return result.Value!;
}

public static async Task<Result<(TFirst, TSecond)>> EnsureSecondNotNull<TFirst, TSecond, TError>(this Task<Result<(TFirst, TSecond?)>> resultTask, TError error) where TError : ErrorBase
{
var result = await resultTask;
return result.EnsureSecondNotNull(error);
}
}
73 changes: 73 additions & 0 deletions RailwayResult.FunctionExtensions/ResultExtensions.Tap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace RailwayResult.FunctionExtensions;

public static partial class ResultExtensions
{
public static Result<TValue> Tap<TValue>(this Result<TValue> result, Action<TValue> func)
{
if (result.IsFailure)
return result;

func(result.Value);
return result;
}

public static async Task<Result<TValue>> Tap<TValue>(this Task<Result<TValue>> resultTask, Action<TValue> func)
{
var result = await resultTask;
return result.Tap(func);
}

public static async Task<Result<TValue>> Tap<TValue>(this Task<Result<Result<TValue>>> resultTask, Action<TValue> func)
{
var result = await resultTask;
if (result.IsFailure)
return result.Error;

return result.Value.Tap(func);
}

public static Result<(TFirst, TSecond)> Tap<TFirst, TSecond>(this Result<(TFirst, TSecond)> result, Action<TFirst, TSecond> func)
{
if (result.IsFailure)
return result;

func(result.Value.Item1, result.Value.Item2);
return result;
}

public static async Task<Result<(TFirst, TSecond)>> Tap<TFirst, TSecond>(this Task<Result<(TFirst, TSecond)>> resultTask, Action<TFirst, TSecond> func)
{
var result = await resultTask;
return result.Tap(func);
}

public static async Task<Result> TapAsync(this Result result, Func<Task> asyncFunc)
{
if (result.IsFailure)
return result.Error;

await asyncFunc();
return Result.Success;
}

public static async Task<Result> TapAsync(this Task<Result> resultTask, Func<Task> asyncFunc)
{
var result = await resultTask;
return await result.TapAsync(asyncFunc);
}

public static async Task<Result<TValue>> TapAsync<TValue>(this Result<TValue> result, Func<TValue, Task> asyncFunc)
{
if (result.IsFailure)
return result;

await asyncFunc(result.Value);
return result;
}

public static async Task<Result<TValue>> TapAsync<TValue>(this Task<Result<TValue>> resultTask, Func<TValue, Task> asyncFunc)
{
var result = await resultTask;
return await result.TapAsync(asyncFunc);
}
}
Loading

0 comments on commit b9a83e6

Please sign in to comment.