Skip to content

Commit

Permalink
feature: add Catch method
Browse files Browse the repository at this point in the history
  • Loading branch information
daht-x authored Dec 4, 2023
1 parent 779a1d3 commit 57755ee
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 7 deletions.
31 changes: 31 additions & 0 deletions source/Monads/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,37 @@ namespace Daht.Sagitta.Core.Monads;
/// <summary>Reference point to initialize <see cref="Result{TFailure, TSuccess}"/>.</summary>
public static class Result
{
/// <summary>Creates a new failed result if the value of <paramref name="createSuccess"/> throws <typeparamref name="TException"/>; otherwise, creates a new successful result.</summary>
/// <typeparam name="TException">Type of possible exception.</typeparam>
/// <typeparam name="TFailure">Type of possible failure.</typeparam>
/// <typeparam name="TSuccess">Type of expected success.</typeparam>
/// <param name="createSuccess">
/// <para>Creates the expected success.</para>
/// <para>If <paramref name="createSuccess"/> is <see langword="null"/> or its value is <see langword="null"/>, <seealso cref="ArgumentNullException"/> will be thrown.</para>
/// </param>
/// <param name="createFailure">
/// <para>Creates the possible failure in combination with <typeparamref name="TException"/>.</para>
/// <para>If <paramref name="createFailure"/> is <see langword="null"/> or its value is <see langword="null"/>, <seealso cref="ArgumentNullException"/> will be thrown.</para>
/// </param>
/// <returns>A new failed result if the value of <paramref name="createSuccess"/> throws <typeparamref name="TException"/>; otherwise, a new successful result.</returns>
/// <exception cref="ArgumentNullException"/>
public static Result<TFailure, TSuccess> Catch<TException, TFailure, TSuccess>(Func<TSuccess> createSuccess, Func<TException, TFailure> createFailure)
where TException : Exception
where TFailure : notnull
where TSuccess : notnull
{
try
{
return Succeed<TFailure, TSuccess>(createSuccess);
}
catch (TException exception)
{
ArgumentNullException.ThrowIfNull(createFailure);
TFailure failure = createFailure(exception) ?? throw new ArgumentNullException(nameof(createFailure));
return Fail<TFailure, TSuccess>(failure);
}
}

/// <summary>Creates a new failed result.</summary>
/// <typeparam name="TFailure">Type of possible failure.</typeparam>
/// <typeparam name="TSuccess">Type of expected success.</typeparam>
Expand Down
5 changes: 2 additions & 3 deletions source/Sagitta.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<EnablePackageValidation>true</EnablePackageValidation>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTarget>Core</PackageTarget>
<Maintainer>David Andrés Hernández Triana</Maintainer>
<Authors>$(Maintainer)</Authors>
<Description>Functional paradigm abstractions | $(PackageTarget)</Description>
<Description>Functional paradigm abstractions | Core</Description>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageTags>$(Company); Sagitta; Functional paradigm; $(PackageTarget)</PackageTags>
<PackageTags>$(Company); Sagitta; Functional; Result</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/daht-x/sagitta-core</RepositoryUrl>
Expand Down
100 changes: 99 additions & 1 deletion test/unit/Monads/ResultTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,108 @@ public sealed class ResultTest
{
private const string root = nameof(Result);

private const string succeed = nameof(Result.Succeed);
private const string @catch = nameof(Result.Catch);

private const string fail = nameof(Result.Fail);

private const string succeed = nameof(Result.Succeed);

#region Catch

[Fact]
[Trait(root, @catch)]
public void Catch_NullCreateSuccessPlusCreateFailure_ArgumentNullException()
{
//Arrange
const Func<string> createSuccess = null!;
Func<InvalidOperationException, string> createFailure = static exception => exception.Message;

//Act
ArgumentNullException? actualException = ExceptionHandler.Catch<ArgumentNullException>(() => _ = Result.Catch(createSuccess, createFailure));

//Assert
ArgumentNullExceptionAsserter.AreEqualParameterNames(nameof(createSuccess), actualException);
}

[Fact]
[Trait(root, @catch)]
public void Catch_CreateSuccessWithNullValuePlusCreateFailure_ArgumentNullException()
{
//Arrange
Func<string> createSuccess = static () => null!;
Func<InvalidOperationException, string> createFailure = static exception => exception.Message;

//Act
ArgumentNullException? actualException = ExceptionHandler.Catch<ArgumentNullException>(() => _ = Result.Catch(createSuccess, createFailure));

//Assert
ArgumentNullExceptionAsserter.AreEqualParameterNames(nameof(createSuccess), actualException);
}

[Fact]
[Trait(root, @catch)]
public void Catch_CreateSuccessPlusCreateFailure_SuccessfulResult()
{
//Arrange
const string expectedSuccess = ResultFixture.Success;
Func<string> createSuccess = static () => expectedSuccess;
Func<InvalidOperationException, string> createFailure = static exception => exception.Message;

//Act
Result<string, string> actualResult = Result.Catch(createSuccess, createFailure);

//Assert
ResultAsserter.AreSuccessful(expectedSuccess, actualResult);
}

[Fact]
[Trait(root, @catch)]
public void Catch_ExceptionPlusNullCreateFailure_ArgumentNullException()
{
//Arrange
Func<string> createSuccess = static () => throw new InvalidOperationException();
const Func<InvalidOperationException, string> createFailure = null!;

//Act
ArgumentNullException? actualException = ExceptionHandler.Catch<ArgumentNullException>(() => _ = Result.Catch(createSuccess, createFailure));

//Assert
ArgumentNullExceptionAsserter.AreEqualParameterNames(nameof(createFailure), actualException);
}

[Fact]
[Trait(root, @catch)]
public void Catch_ExceptionPlusCreateFailureWithNullValue_ArgumentNullException()
{
//Arrange
Func<string> createSuccess = static () => throw new InvalidOperationException();
Func<InvalidOperationException, string> createFailure = static _ => null!;

//Act
ArgumentNullException? actualException = ExceptionHandler.Catch<ArgumentNullException>(() => _ = Result.Catch(createSuccess, createFailure));

//Assert
ArgumentNullExceptionAsserter.AreEqualParameterNames(nameof(createFailure), actualException);
}

[Fact]
[Trait(root, @catch)]
public void Catch_ExceptionPlusCreateFailure_FailedResult()
{
//Arrange
Func<string> createSuccess = static () => throw new InvalidOperationException();
Func<InvalidOperationException, string> createFailure = static exception => exception.Message;
const string expectedFailure = "Operation is not valid due to the current state of the object.";

//Act
Result<string, string> actualResult = Result.Catch(createSuccess, createFailure);

//Assert
ResultAsserter.AreFailed(expectedFailure, actualResult);
}

#endregion

#region Fail

#region Overload No. 01
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ namespace Daht.Sagitta.Core.UnitTest.Shared.Exceptions.Asserters;

internal static class ArgumentNullExceptionAsserter
{
internal static void AreEqualParameterNames(string expectedParameterName, ArgumentNullException? exception)
internal static void AreEqualParameterNames(string expectedParameterName, ArgumentNullException? actualException)
{
Assert.NotNull(exception);
Assert.Equal(expectedParameterName, exception.ParamName);
Assert.NotNull(actualException);
Assert.Equal(expectedParameterName, actualException.ParamName);
}
}

0 comments on commit 57755ee

Please sign in to comment.