Skip to content

Commit

Permalink
Inital structure, basic abstractions and tests, CI workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
skrasekmichael committed Mar 16, 2024
1 parent 3e23c0d commit d5ef818
Show file tree
Hide file tree
Showing 17 changed files with 677 additions and 0 deletions.
394 changes: 394 additions & 0 deletions .editorconfig

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# automatically normalize line endings
* text=auto eol=crlf

36 changes: 36 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI

env:
SOLUTION: RailwayResult.sln
DOTNET_NOLOGO: true # Disable the .NET logo
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable the .NET first time experience
DOTNET_CLI_TELEMETRY_OPTOUT: true # Disable sending .NET CLI telemetry

on:
workflow_dispatch:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
build:
name: Build & Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x

- name: Restore
run: dotnet restore ${{ env.SOLUTION }}

- name: Build
run: dotnet build ${{ env.SOLUTION }} -c Release --no-restore

- name: Test
run: dotnet test ${{env.SOLUTION }} -c Release --no-build --verbosity minimal
10 changes: 10 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>

12 changes: 12 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project>
<PropertyGroup>
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="xunit" Version="2.5.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
</Project>
46 changes: 46 additions & 0 deletions RailwayResult.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D27FC398-A542-46F4-A335-C6D1A7C73F36}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RailwayResult", "src\RailwayResult\RailwayResult.csproj", "{C290883E-72EC-433E-BB3F-83B58381F9FF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9AF71DD6-A91C-429D-9E17-91DB64571176}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RailwayResult.Tests", "tests\RailwayResult.Tests\RailwayResult.Tests.csproj", "{BF48CBB9-7B8A-4376-9094-D71B093E0660}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A8011620-9912-4139-B2A2-72C334F2DB82}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C290883E-72EC-433E-BB3F-83B58381F9FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C290883E-72EC-433E-BB3F-83B58381F9FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C290883E-72EC-433E-BB3F-83B58381F9FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C290883E-72EC-433E-BB3F-83B58381F9FF}.Release|Any CPU.Build.0 = Release|Any CPU
{BF48CBB9-7B8A-4376-9094-D71B093E0660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF48CBB9-7B8A-4376-9094-D71B093E0660}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF48CBB9-7B8A-4376-9094-D71B093E0660}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF48CBB9-7B8A-4376-9094-D71B093E0660}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C290883E-72EC-433E-BB3F-83B58381F9FF} = {D27FC398-A542-46F4-A335-C6D1A7C73F36}
{BF48CBB9-7B8A-4376-9094-D71B093E0660} = {9AF71DD6-A91C-429D-9E17-91DB64571176}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54E35DCC-CE9C-4B2C-A050-D7E86E862BDD}
EndGlobalSection
EndGlobal
13 changes: 13 additions & 0 deletions src/RailwayResult/Error.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace RailwayResult;

public abstract record Error<TSelf> : ErrorBase where TSelf : Error<TSelf>, new()
{
public static TSelf New(string message, string key = "")
{
return new TSelf()
{
Key = key,
Message = message
};
}
}
7 changes: 7 additions & 0 deletions src/RailwayResult/ErrorBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace RailwayResult;

public abstract record ErrorBase
{
public string Key { get; protected init; } = null!;
public string Message { get; protected init; } = null!;
}
6 changes: 6 additions & 0 deletions src/RailwayResult/Exceptions/AccesingFailedResulsException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace RailwayResult.Exceptions;

public sealed class AccessingValueOfFailureResultException : InvalidOperationException
{
public AccessingValueOfFailureResultException() : base("Value of failure result cannot be accessed.") { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace RailwayResult.Exceptions;

public sealed class AccessingErrorOfSuccessResultException : InvalidOperationException
{
public AccessingErrorOfSuccessResultException() : base("Error of success result cannot be accessed.") { }
}
1 change: 1 addition & 0 deletions src/RailwayResult/RailwayResult.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Project Sdk="Microsoft.NET.Sdk" />
39 changes: 39 additions & 0 deletions src/RailwayResult/Result.Generic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using RailwayResult.Exceptions;

namespace RailwayResult;

public sealed class Result<TValue>
{
public bool IsSuccess { get; }
public bool IsFailure => !IsSuccess;

private readonly TValue? _value;
public TValue Value => IsSuccess ? _value! : throw new AccessingValueOfFailureResultException();

private readonly ErrorBase? _error = null;
public ErrorBase Error => IsFailure ? _error! : throw new AccessingErrorOfSuccessResultException();

private Result(ErrorBase error)
{
IsSuccess = false;
_error = error;
_value = default;
}

private Result(TValue? value)
{
IsSuccess = true;
_error = null;
_value = value;
}

public TOut Match<TOut>(Func<TValue, TOut> successMap, Func<ErrorBase, TOut> failureMap)
{
return IsSuccess ? successMap(_value!) : failureMap(_error!);
}

public static Result<TValue> Success(TValue value) => new(value);

public static implicit operator Result<TValue>(TValue? value) => new(value);
public static implicit operator Result<TValue>(ErrorBase error) => new(error);
}
28 changes: 28 additions & 0 deletions src/RailwayResult/Result.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using RailwayResult.Exceptions;

namespace RailwayResult;

public sealed class Result
{
public bool IsSuccess { get; }
public bool IsFailure => !IsSuccess;

private readonly ErrorBase? _error = null;
public ErrorBase Error => IsFailure ? _error! : throw new AccessingErrorOfSuccessResultException();

public Result(ErrorBase error)
{
IsSuccess = false;
_error = error;
}

private Result()
{
IsSuccess = true;
_error = null;
}

public static readonly Result Success = new();

public static implicit operator Result(ErrorBase error) => new(error);
}
8 changes: 8 additions & 0 deletions tests/RailwayResult.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
global using FluentAssertions;

global using RailwayResult;
global using RailwayResult.Exceptions;

global using RailwayResults.Tests.Mocks;

global using Xunit;
5 changes: 5 additions & 0 deletions tests/RailwayResult.Tests/Mocks/GenericError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using RailwayResult;

namespace RailwayResults.Tests.Mocks;

public sealed record GenericError : Error<GenericError>;
20 changes: 20 additions & 0 deletions tests/RailwayResult.Tests/RailwayResult.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>

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

</Project>
43 changes: 43 additions & 0 deletions tests/RailwayResult.Tests/ResultTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace RailwayResult.Tests;

public sealed class ResultTests
{
[Fact]
public void AccessingValueOfFailureResult_Should_ResultInException()
{
//arrange
Result<string> failedResult = GenericError.New("Error");

//act
var accessor = () => failedResult.Value;

//assert
accessor.Should().ThrowExactly<AccessingValueOfFailureResultException>();
}

[Fact]
public void AccessingErrorOfSuccessResult_Should_ResultInException()
{
//arrange
Result result = Result.Success;

//act
var accessor = () => result.Error;

//assert
accessor.Should().ThrowExactly<AccessingErrorOfSuccessResultException>();
}

[Fact]
public void AccessingErrorOfGenericSuccessResult_Should_ResultInException()
{
//arrange
Result<string> result = "Success";

//act
var accessor = () => result.Error;

//assert
accessor.Should().ThrowExactly<AccessingErrorOfSuccessResultException>();
}
}

0 comments on commit d5ef818

Please sign in to comment.