-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat; re-introduced functional structures to help with upcoming PrcsMngr
- Loading branch information
1 parent
f41b4b0
commit 0288184
Showing
4 changed files
with
355 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
namespace Ecommerce.Core.Structures; | ||
|
||
public record Either<TLeft, TRight> | ||
{ | ||
public Maybe<TLeft> Left { get; } | ||
public Maybe<TRight> Right { get; } | ||
|
||
public Either(TLeft value) | ||
{ | ||
Left = Maybe<TLeft>.Of(value); | ||
Right = Maybe<TRight>.Empty; | ||
} | ||
|
||
public Either(TRight value) | ||
{ | ||
Left = Maybe<TLeft>.Empty; | ||
Right = Maybe<TRight>.Of(value); | ||
} | ||
|
||
public Either(Maybe<TLeft> left, Maybe<TRight> right) | ||
{ | ||
if (!left.IsPresent && !right.IsPresent) | ||
throw new ArgumentOutOfRangeException(nameof(right)); | ||
|
||
Left = left; | ||
Right = right; | ||
} | ||
|
||
public TMapped Map<TMapped>( | ||
Func<TLeft, TMapped> mapLeft, | ||
Func<TRight, TMapped> mapRight) | ||
{ | ||
if (Left.IsPresent) | ||
return mapLeft(Left.GetOrThrow()); | ||
|
||
if (Right.IsPresent) | ||
return mapRight(Right.GetOrThrow()); | ||
|
||
throw new Exception("This should not be possible"); | ||
} | ||
|
||
public void Switch( | ||
Action<TLeft> onLeft, | ||
Action<TRight> onRight) | ||
{ | ||
if (Left.IsPresent) | ||
{ | ||
onLeft(Left.GetOrThrow()); | ||
return; | ||
} | ||
|
||
if (Right.IsPresent) | ||
{ | ||
onRight(Right.GetOrThrow()); | ||
return; | ||
} | ||
|
||
throw new Exception("This should not be possible"); | ||
} | ||
} | ||
|
||
public static class EitherExtensions | ||
{ | ||
public static (TLeft? Left, TRight? Right) AssertAnyDefined<TLeft, TRight>( | ||
this (TLeft? Left, TRight? Right) value) | ||
{ | ||
if (value.Left == null && value.Right == null) | ||
throw new ArgumentOutOfRangeException(nameof(value), "A value has not been set"); | ||
|
||
return value; | ||
} | ||
|
||
public static TMapped Map<TLeft, TRight, TMapped>( | ||
this (TLeft? Left, TRight? Right) value, | ||
Func<TLeft, TMapped> mapLeft, | ||
Func<TRight, TMapped> mapRight) | ||
where TLeft: struct | ||
where TRight: struct | ||
{ | ||
var (left, right) = value.AssertAnyDefined(); | ||
|
||
if (left.HasValue) | ||
return mapLeft(left.Value); | ||
|
||
if (right.HasValue) | ||
return mapRight(right.Value); | ||
|
||
throw new Exception("This should not be possible"); | ||
} | ||
|
||
public static TMapped Map<TLeft, TRight, TMapped>( | ||
this (TLeft? Left, TRight? Right) value, | ||
Func<TLeft, TMapped> mapT1, | ||
Func<TRight, TMapped> mapT2) | ||
{ | ||
value.AssertAnyDefined(); | ||
|
||
var either = value.Left != null | ||
? new Either<TLeft, TRight>(value.Left!) | ||
: new Either<TLeft, TRight>(value.Right!); | ||
|
||
return either.Map(mapT1, mapT2); | ||
} | ||
|
||
public static void Switch<TLeft, TRight>( | ||
this (TLeft? Left, TRight? Right) value, | ||
Action<TLeft> onT1, | ||
Action<TRight> onT2) | ||
{ | ||
value.AssertAnyDefined(); | ||
|
||
var either = value.Left != null | ||
? new Either<TLeft, TRight>(value.Left!) | ||
: new Either<TLeft, TRight>(value.Right!); | ||
|
||
either.Switch(onT1, onT2); | ||
} | ||
|
||
public static (TLeft?, TRight?) Either<TLeft, TRight>( | ||
TLeft? left = default) | ||
=> (left, default); | ||
|
||
public static (TLeft?, TRight?) Either<TLeft, TRight>( | ||
TRight? right = default) | ||
=> (default, right); | ||
} |
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,44 @@ | ||
namespace Ecommerce.Core.Structures; | ||
|
||
public record Maybe<TSomething> | ||
{ | ||
private readonly TSomething? _value; | ||
public bool IsPresent { get; } | ||
|
||
private Maybe(TSomething value, bool isPresent) | ||
{ | ||
_value = value; | ||
IsPresent = isPresent; | ||
} | ||
|
||
public static readonly Maybe<TSomething> Empty = new(default!, false); | ||
|
||
public static Maybe<TSomething> Of(TSomething value) => | ||
value != null ? new Maybe<TSomething>(value, true) : Empty; | ||
|
||
public static Maybe<TSomething> If(bool check, Func<TSomething> getValue) => | ||
check ? new Maybe<TSomething>(getValue(), true) : Empty; | ||
|
||
public TSomething GetOrThrow() => | ||
IsPresent ? _value! : throw new ArgumentNullException(nameof(_value)); | ||
|
||
public TSomething GetOrDefault(TSomething defaultValue = default!) => | ||
IsPresent ? _value ?? defaultValue : defaultValue; | ||
|
||
public void IfExists(Action<TSomething> perform) | ||
{ | ||
if (IsPresent) | ||
{ | ||
perform(_value!); | ||
} | ||
} | ||
} | ||
|
||
public static class Maybe | ||
{ | ||
public static Maybe<TSomething> Of<TSomething>(TSomething value) => | ||
Maybe<TSomething>.Of(value); | ||
|
||
public static Maybe<TSomething> If<TSomething>(bool check, Func<TSomething> getValue) => | ||
Maybe<TSomething>.If(check, getValue); | ||
} |
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,106 @@ | ||
namespace Ecommerce.Core.Structures; | ||
|
||
public record OneOf<T1, T2, T3> | ||
{ | ||
public Maybe<T1> First { get; } | ||
public Maybe<T2> Second { get; } | ||
public Maybe<T3> Third { get; } | ||
|
||
public OneOf(T1 value) | ||
{ | ||
First = Maybe<T1>.Of(value); | ||
Second = Maybe<T2>.Empty; | ||
Third = Maybe<T3>.Empty; | ||
} | ||
|
||
public OneOf(T2 value) | ||
{ | ||
First = Maybe<T1>.Empty; | ||
Second = Maybe<T2>.Of(value); | ||
Third = Maybe<T3>.Empty; | ||
} | ||
|
||
public OneOf(T3 value) | ||
{ | ||
First = Maybe<T1>.Empty; | ||
Second = Maybe<T2>.Empty; | ||
Third = Maybe<T3>.Of(value); | ||
} | ||
|
||
public OneOf((T1? First, T2? Second, T3? Third) value) | ||
{ | ||
First = value.First != null ? Maybe<T1>.Of(value.First) : Maybe<T1>.Empty; | ||
Second = value.Second != null ? Maybe<T2>.Of(value.Second) : Maybe<T2>.Empty; | ||
Third = value.Third != null ? Maybe<T3>.Of(value.Third) : Maybe<T3>.Empty; | ||
} | ||
|
||
public TMapped Map<TMapped>( | ||
Func<T1, TMapped> mapT1, | ||
Func<T2, TMapped> mapT2, | ||
Func<T3, TMapped> mapT3) | ||
{ | ||
if (First.IsPresent) | ||
{ | ||
return mapT1(First.GetOrThrow()); | ||
} | ||
|
||
if (Second.IsPresent) | ||
{ | ||
return mapT2(Second.GetOrThrow()); | ||
} | ||
|
||
if (Third.IsPresent) | ||
{ | ||
return mapT3(Third.GetOrThrow()); | ||
} | ||
|
||
throw new Exception("This should not be possible"); | ||
} | ||
|
||
public void Switch( | ||
Action<T1> onT1, | ||
Action<T2> onT2, | ||
Action<T3> onT3) | ||
{ | ||
if (First.IsPresent) | ||
{ | ||
onT1(First.GetOrThrow()); | ||
return; | ||
} | ||
|
||
if (Second.IsPresent) | ||
{ | ||
onT2(Second.GetOrThrow()); | ||
return; | ||
} | ||
|
||
if (Third.IsPresent) | ||
{ | ||
onT3(Third.GetOrThrow()); | ||
return; | ||
} | ||
|
||
throw new Exception("This should not be possible"); | ||
} | ||
} | ||
|
||
public static class OneOfExtensions | ||
{ | ||
public static void Map<T1, T2, T3, TMapped>( | ||
this (T1? First, T2? Second, T3? Third) value, | ||
Func<T1, TMapped> mapT1, | ||
Func<T2, TMapped> mapT2, | ||
Func<T3, TMapped> mapT3) | ||
{ | ||
new OneOf<T1, T2, T3>(value).Map(mapT1, mapT2, mapT3); | ||
} | ||
|
||
public static void Switch<T1, T2, T3, TMapped>( | ||
this (T1? First, T2? Second, T3? Third) value, | ||
Action<T1> onT1, | ||
Action<T2> onT2, | ||
Action<T3> onT3) | ||
{ | ||
new OneOf<T1, T2, T3>(value).Switch(onT1, onT2, onT3); | ||
} | ||
} |
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,79 @@ | ||
namespace Ecommerce.Core.Structures; | ||
|
||
public record Result<TSuccess, TError> | ||
{ | ||
public Maybe<TSuccess> Success { get; } | ||
public Maybe<TError> Error { get; } | ||
|
||
public Result(TSuccess value) | ||
{ | ||
Success = Maybe<TSuccess>.Of(value); | ||
Error = Maybe<TError>.Empty; | ||
} | ||
|
||
public Result(TError value) | ||
{ | ||
Success = Maybe<TSuccess>.Empty; | ||
Error = Maybe<TError>.Of(value); | ||
} | ||
|
||
public Result(Maybe<TSuccess> success, Maybe<TError> error) | ||
{ | ||
if (!success.IsPresent && !error.IsPresent) | ||
throw new ArgumentOutOfRangeException(nameof(error)); | ||
|
||
Success = success; | ||
Error = error; | ||
} | ||
|
||
public TMapped Map<TMapped>( | ||
Func<TSuccess, TMapped> mapSuccess, | ||
Func<TError, TMapped> mapFailure | ||
) | ||
{ | ||
if (Success.IsPresent) | ||
return mapSuccess(Success.GetOrThrow()); | ||
|
||
if (Error.IsPresent) | ||
return mapFailure(Error.GetOrThrow()); | ||
|
||
throw new Exception("That should never happen!"); | ||
} | ||
|
||
public object FlatMap() | ||
{ | ||
if (Success.IsPresent) | ||
return Success.GetOrThrow()!; | ||
|
||
if (Error.IsPresent) | ||
return Error.GetOrThrow()!; | ||
|
||
throw new Exception("That should never happen!"); | ||
} | ||
|
||
public void Switch( | ||
Action<TSuccess> onSuccess, | ||
Action<TError> onFailure) | ||
{ | ||
if (Success.IsPresent) | ||
{ | ||
onSuccess(Success.GetOrThrow()); | ||
return; | ||
} | ||
|
||
if (Error.IsPresent) | ||
{ | ||
onFailure(Error.GetOrThrow()); | ||
return; | ||
} | ||
|
||
throw new Exception("That should never happen!"); | ||
} | ||
} | ||
|
||
public static class Result | ||
{ | ||
public static Result<TSuccess, TError> Success<TSuccess, TError>(TSuccess success) => new(success); | ||
|
||
public static Result<TSuccess, TError> Failure<TSuccess, TError>(TError error) => new(error); | ||
} |