Skip to content

Commit

Permalink
feature: replace Result<TSuccess, TFailure> type from struct to `…
Browse files Browse the repository at this point in the history
…class`
  • Loading branch information
daht-x committed Dec 20, 2023
1 parent 86e3595 commit f024f53
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 73 deletions.
43 changes: 23 additions & 20 deletions source/Monads/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,35 @@ namespace Daht.Sagitta.Core.Monads;
/// <summary>Type that encapsulates both the expected success and the possible failure of a given action.</summary>
/// <typeparam name="TSuccess">Type of expected success.</typeparam>
/// <typeparam name="TFailure">Type of possible failure.</typeparam>
public readonly record struct Result<TSuccess, TFailure>
public sealed class Result<TSuccess, TFailure>
{
/// <summary>Indicates whether the status is successful or <see langword="default"/>.</summary>
public bool IsSuccessfulOrDefault => IsSuccessful || IsDefault;

/// <summary>Indicates whether the status is failed or <see langword="default"/>.</summary>
public bool IsFailedOrDefault => IsFailed || IsDefault;

/// <summary>Indicates whether the status is <see langword="default"/>.</summary>
public bool IsDefault => this is
{
IsSuccessful: false,
IsFailed: false
};

/// <summary>Indicates whether the status is successful.</summary>
public bool IsSuccessful { get; internal init; }
public bool IsSuccessful { get; }

/// <summary>The expected success.</summary>
public TSuccess Success { get; internal init; }
public TSuccess Success { get; } = default!;

/// <summary>Indicates whether the status is failed.</summary>
public bool IsFailed { get; internal init; }
public bool IsFailed { get; }

/// <summary>The possible failure.</summary>
public TFailure Failure { get; internal init; }
public TFailure Failure { get; } = default!;

/// <summary>Creates a new successful result.</summary>
/// <param name="success">The expected success.</param>
public Result(TSuccess success)
{
IsSuccessful = true;
Success = success;
}

/// <summary>Creates a new failed result.</summary>
/// <param name="failure">The possible failure.</param>
public Result(TFailure failure)
{
IsFailed = true;
Failure = failure;
}

/// <summary>Creates a new successful result.</summary>
/// <param name="success">The expected success.</param>
Expand All @@ -48,11 +51,11 @@ namespace Daht.Sagitta.Core.Monads;
/// <returns>A new failed result if the value of <paramref name="predicate"/> is <see langword="true"/>; otherwise, the previous result.</returns>
public Result<TSuccess, TFailure> Ensure([NotNull] Func<TSuccess, bool> predicate, TFailure failure)
{
if (IsFailedOrDefault)
if (IsFailed)
{
return this;
}
if (predicate(Success))
else if (predicate(Success))
{
return ResultFactory.Fail<TSuccess, TFailure>(failure);
}
Expand Down
16 changes: 4 additions & 12 deletions source/Monads/ResultFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,29 @@ public static class ResultFactory
/// <param name="success">The expected success.</param>
/// <returns>A new successful result.</returns>
public static Result<TSuccess, TFailure> Succeed<TSuccess, TFailure>(TSuccess success)
=> new()
{
IsSuccessful = true,
Success = success
};
=> new(success);

/// <summary>Creates a new successful result.</summary>
/// <typeparam name="TSuccess">Type of expected success.</typeparam>
/// <typeparam name="TFailure">Type of possible failure.</typeparam>
/// <param name="createSuccess">Creates the expected success.</param>
/// <returns>A new successful result.</returns>
public static Result<TSuccess, TFailure> Succeed<TSuccess, TFailure>([NotNull] Func<TSuccess> createSuccess)
=> Succeed<TSuccess, TFailure>(createSuccess());
=> new(createSuccess());

/// <summary>Creates a new failed result.</summary>
/// <typeparam name="TSuccess">Type of expected success.</typeparam>
/// <typeparam name="TFailure">Type of possible failure.</typeparam>
/// <param name="failure">The possible failure.</param>
/// <returns>A new failed result.</returns>
public static Result<TSuccess, TFailure> Fail<TSuccess, TFailure>(TFailure failure)
=> new()
{
IsFailed = true,
Failure = failure
};
=> new(failure);

/// <summary>Creates a new failed result.</summary>
/// <typeparam name="TSuccess">Type of expected success.</typeparam>
/// <typeparam name="TFailure">Type of possible failure.</typeparam>
/// <param name="createFailure">Creates the possible failure.</param>
/// <returns>A new failed result.</returns>
public static Result<TSuccess, TFailure> Fail<TSuccess, TFailure>([NotNull] Func<TFailure> createFailure)
=> Fail<TSuccess, TFailure>(createFailure());
=> new(createFailure());
}
2 changes: 1 addition & 1 deletion source/Sagitta.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<Description>Functional paradigm abstractions | Core</Description>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageTags>$(Company); Sagitta; Functional-paradigm; Result</PackageTags>
<PackageTags>$(Company); Sagitta; Functional; Paradigm; Result</PackageTags>
<NeutralLanguage>en-US</NeutralLanguage>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/daht-x/sagitta-core</RepositoryUrl>
Expand Down
16 changes: 0 additions & 16 deletions test/unit/Monads/Asserters/ResultAsserter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,8 @@ namespace Daht.Sagitta.Core.UnitTest.Monads.Asserters;

internal static class ResultAsserter
{
internal static void IsDefault<TSuccess, TFailure>(Result<TSuccess, TFailure> actualResult)
{
Assert.True(actualResult.IsSuccessfulOrDefault);
Assert.True(actualResult.IsFailedOrDefault);
Assert.True(actualResult.IsDefault);
Assert.False(actualResult.IsSuccessful);
Assert.Equal(default, actualResult.Success);
Assert.False(actualResult.IsFailed);
Assert.Equal(default, actualResult.Failure);
}
internal static void AreSuccessful<TSuccess, TFailure>(TSuccess expectedSuccess, Result<TSuccess, TFailure> actualResult)
{
Assert.True(actualResult.IsSuccessfulOrDefault);
Assert.False(actualResult.IsFailedOrDefault);
Assert.False(actualResult.IsDefault);
Assert.True(actualResult.IsSuccessful);
Assert.Equal(expectedSuccess, actualResult.Success);
Assert.False(actualResult.IsFailed);
Expand All @@ -25,9 +12,6 @@ internal static class ResultAsserter

internal static void AreFailed<TSuccess, TFailure>(TFailure expectedFailure, Result<TSuccess, TFailure> actualResult)
{
Assert.False(actualResult.IsSuccessfulOrDefault);
Assert.True(actualResult.IsFailedOrDefault);
Assert.False(actualResult.IsDefault);
Assert.False(actualResult.IsSuccessful);
Assert.Equal(default, actualResult.Success);
Assert.True(actualResult.IsFailed);
Expand Down
2 changes: 0 additions & 2 deletions test/unit/Monads/Mothers/ResultMother.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ namespace Daht.Sagitta.Core.UnitTest.Monads.Mothers;

internal static class ResultMother
{
internal static Result<Constellation, string> Default => default;

internal static Result<Constellation, string> Succeed()
=> ResultFactory.Succeed<Constellation, string>(ResultFixture.Success);

Expand Down
8 changes: 4 additions & 4 deletions test/unit/Monads/ResultFactoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void Catch_ExceptionPlusCreateFailure_FailedResult()

#region Succeed

#region Overload No. 01
#region Overload

[Fact]
[Trait(root, succeed)]
Expand All @@ -66,7 +66,7 @@ public void Succeed_Success_SuccessfulResult()

#endregion

#region Overload No. 02
#region Overload

[Fact]
[Trait(root, succeed)]
Expand All @@ -89,7 +89,7 @@ public void Succeed_CreateSuccess_SuccessfulResult()

#region Fail

#region Overload No. 01
#region Overload

[Fact]
[Trait(root, fail)]
Expand All @@ -107,7 +107,7 @@ public void Fail_Failure_FailedResult()

#endregion

#region Overload No. 02
#region Overload

[Fact]
[Trait(root, fail)]
Expand Down
61 changes: 43 additions & 18 deletions test/unit/Monads/ResultTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,43 @@ public sealed class ResultTest
{
private const string root = nameof(Result<object, object>);

private const string constructor = "Constructor";

private const string implicitOperator = "Implicit Operator";

private const string ensure = nameof(Result<object, object>.Ensure);

#region Implicit Operator
#region Constructor

#region Overload No. 01
#region Overload

[Fact]
[Trait(root, implicitOperator)]
public void ImplicitOperator_Success_SuccessfulResult()
[Trait(root, constructor)]
public void Constructor_Success_SuccessfulResult()
{
//Arrange
Constellation expectedSuccess = ResultFixture.Success;

//Act
Result<Constellation, string> actualResult = expectedSuccess;
Result<Constellation, string> actualResult = new(expectedSuccess);

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

#endregion

#region Overload No. 02
#region Overload

[Fact]
[Trait(root, implicitOperator)]
public void ImplicitOperator_Failure_FailedResult()
[Trait(root, constructor)]
public void Constructor_Failure_FailedResult()
{
//Arrange
const string expectedFailure = ResultFixture.Failure;

//Act
Result<Constellation, string> actualResult = expectedFailure;
Result<Constellation, string> actualResult = new(expectedFailure);

//Assert
ResultAsserter.AreFailed(expectedFailure, actualResult);
Expand All @@ -48,25 +50,48 @@ public void ImplicitOperator_Failure_FailedResult()

#endregion

#region Ensure
#region Implicit Operator

#region Overload

[Fact]
[Trait(root, ensure)]
public void Ensure_DefaultResultPlusFalsePredicatePlusFailure_DefaultResult()
[Trait(root, implicitOperator)]
public void ImplicitOperator_Success_SuccessfulResult()
{
//Arrange
Func<Constellation, bool> predicate = static _ => false;
const string failure = ResultFixture.Failure;
Constellation expectedSuccess = ResultFixture.Success;

//Act
Result<Constellation, string> actualResult = ResultMother
.Default
.Ensure(predicate, failure);
Result<Constellation, string> actualResult = expectedSuccess;

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

#endregion

#region Overload

[Fact]
[Trait(root, implicitOperator)]
public void ImplicitOperator_Failure_FailedResult()
{
//Arrange
const string expectedFailure = ResultFixture.Failure;

//Act
Result<Constellation, string> actualResult = expectedFailure;

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

#endregion

#endregion

#region Ensure

[Fact]
[Trait(root, ensure)]
public void Ensure_FailedResultPlusFalsePredicatePlusFailure_FailedResult()
Expand Down

0 comments on commit f024f53

Please sign in to comment.