From d0e1802eb141100faadd5521c8bb62b2e0d0aab5 Mon Sep 17 00:00:00 2001 From: Thomas Bruderer Date: Wed, 29 May 2024 16:32:47 +0200 Subject: [PATCH] Support Downcast on the Monadic Types (Option, Either, Result) * with tests --- Funcky.Test/DownCastTest.cs | 78 ++++++++++++++++++++++++++++++++++ Funcky/DownCast.cs | 35 +++++++++++++++ Funcky/PublicAPI.Unshipped.txt | 5 ++- 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 Funcky.Test/DownCastTest.cs create mode 100644 Funcky/DownCast.cs diff --git a/Funcky.Test/DownCastTest.cs b/Funcky.Test/DownCastTest.cs new file mode 100644 index 00000000..3dbbfbd5 --- /dev/null +++ b/Funcky.Test/DownCastTest.cs @@ -0,0 +1,78 @@ +namespace Funcky.Test; + +public sealed class DownCastTest +{ + [Fact] + public void DownCastToObjectOfRuntimeTypeReturnsSomeOfTheRequestedType() + { + var option = Option.Some("Hello world!"); + + FunctionalAssert.Some("Hello world!", DownCast.From(option)); + } + + [Fact] + public void DownCastToWrongObjectOfRuntimeTypeReturnsNone() + { + var option = Option.Some(new object()); + + FunctionalAssert.None(DownCast.From(option)); + } + + [Fact] + public void DownCastFromANoneAlwaysGivesNone() + { + var option = Option.None; + + FunctionalAssert.None(DownCast.From(option)); + } + + [Fact] + public void DownCastToObjectOfRuntimeTypeReturnsRightOfTheRequestedType() + { + var either = Either.Right("Hello world!"); + + FunctionalAssert.Right("Hello world!", DownCast.From(either, () => "failed cast!")); + } + + [Fact] + public void DownCastToWrongObjectOfRuntimeTypeReturnsLeft() + { + var either = Either.Right(new object()); + + FunctionalAssert.Left("failed cast!", DownCast.From(either, () => "failed cast!")); + } + + [Fact] + public void DownCastFromALeftAlwaysGivesTheOldLeft() + { + var either = Either.Left("initial left state!"); + + FunctionalAssert.Left("initial left state!", DownCast.From(either, () => "failed cast!")); + } + + [Fact] + public void DownCastToObjectOfRuntimeTypeReturnsOkOfTheRequestedType() + { + var result = Result.Ok("Hello world!"); + + FunctionalAssert.Ok("Hello world!", DownCast.From(result)); + } + + [Fact] + public void DownCastToWrongObjectOfRuntimeTypeReturnsError() + { + var result = Result.Ok(new object()); + + var exception = FunctionalAssert.Error(DownCast.From(result)); + Assert.IsType(exception); + } + + [Fact] + public void DownCastFromAnErrorAlwaysGivesTheOldError() + { + var result = Result.Error(new FileNotFoundException()); + + var exception = FunctionalAssert.Error(DownCast.From(result)); + Assert.IsType(exception); + } +} diff --git a/Funcky/DownCast.cs b/Funcky/DownCast.cs new file mode 100644 index 00000000..c4c77696 --- /dev/null +++ b/Funcky/DownCast.cs @@ -0,0 +1,35 @@ +namespace Funcky; + +public static class DownCast + where TResult : class +{ + public static Option From(Option option) + where TItem : class + => option.SelectMany(OptionDownCast); + + public static Result From(Result result) + where TItem : class + => result.SelectMany(ResultDownCast); + + public static Either From(Either either, Func failedCast) + where TRight : class + where TLeft : notnull + => either.SelectMany(right => EitherDownCast(failedCast, right)); + + private static Option OptionDownCast(TItem item) + where TItem : class + => Option.FromNullable(item as TResult); + + private static Result ResultDownCast(TValidResult result) + where TValidResult : class + => result as TResult is { } validResult + ? Result.Ok(validResult) + : Result.Error(new InvalidCastException()); + + private static Either EitherDownCast(Func failedCast, TRight right) + where TRight : class + where TLeft : notnull + => right as TResult is { } result + ? Either.Right(result) + : Either.Left(failedCast()); +} diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index 074c6ad1..ad71e021 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -1,2 +1,5 @@ #nullable enable - +Funcky.DownCast +static Funcky.DownCast.From(Funcky.Monads.Option option) -> Funcky.Monads.Option +static Funcky.DownCast.From(Funcky.Monads.Result result) -> Funcky.Monads.Result +static Funcky.DownCast.From(Funcky.Monads.Either either, System.Func! failedCast) -> Funcky.Monads.Either