├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── release.yml └── workflows │ ├── docs.yml │ ├── publish.yml │ └── test.yml ├── .gitignore ├── AGENTS.md ├── Banner.png ├── EXPLAIN.md ├── Icon.png ├── LICENSE.md ├── LightResults.sln ├── LightResults.sln.DotSettings ├── README.md ├── docfx ├── docfx.json ├── docs │ ├── breaking-changes.md │ ├── getting-started.md │ ├── performance.md │ └── toc.yml ├── googlec98a83d0d9926e87.html ├── images │ ├── Icon.png │ └── Logo.png ├── index.md └── toc.yml ├── readme.txt ├── src └── LightResults │ ├── .editorconfig │ ├── Common │ ├── IActionableResult.cs │ ├── SingleItemMetadataDictionary.cs │ └── StringHelper.cs │ ├── Error.cs │ ├── IError.cs │ ├── IResult.cs │ ├── IResult`1.cs │ ├── LightResults.csproj │ ├── Result.cs │ └── Result`1.cs ├── tests └── LightResults.Tests │ ├── ActionableResultTests.cs │ ├── CustomErrorTests.cs │ ├── ErrorTests.cs │ ├── LightResults.Tests.csproj │ ├── ResultTValueTests.cs │ └── ResultTests.cs └── tools ├── LightResults.ComparisonBenchmarks ├── Benchmarks.A_LightResults.cs ├── Benchmarks.B_FluentResults.cs ├── Benchmarks.C_ArdalisResult.cs ├── Benchmarks.D_SimpleResults.cs ├── Benchmarks.E_Rascal.cs ├── Benchmarks.cs ├── LightResults.ComparisonBenchmarks.csproj └── Program.cs ├── LightResults.CurrentBenchmarks ├── Benchmarks.cs ├── LightResults.CurrentBenchmarks.csproj └── Program.cs └── LightResults.DevelopBenchmarks ├── Benchmarks.cs ├── LightResults.DevelopBenchmarks.csproj ├── Program.cs └── Working.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | trim_trailing_whitespace = true 6 | end_of_line = lf 7 | insert_final_newline = true 8 | max_line_length = 160 9 | 10 | [*.cs] 11 | indent_style = space 12 | indent_size = 4 13 | 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | .config text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 3 | .cs text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 4 | .csproj text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 5 | .dist text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 6 | .DotSettings text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 7 | .editorconfig text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 8 | .gitattributes text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 9 | .gitignore text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 10 | .info text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 11 | .js text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 12 | .json text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 13 | .md text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 14 | .ps1 text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 15 | .props text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 16 | .resx text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 17 | .runsettings text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 18 | .sln text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 19 | .targets text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 20 | .txt text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 21 | .yml text eol=lf encoding=UTF-8 working-tree-encoding=UTF-8 22 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jscarle 2 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: Changes 4 | labels: 5 | - "*" 6 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: false 17 | 18 | jobs: 19 | deploy: 20 | environment: 21 | name: github-pages 22 | url: ${{ steps.deployment.outputs.page_url }} 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | 28 | - name: Setup Pages 29 | uses: actions/configure-pages@v4 30 | 31 | - name: Setup .NET 9.0 32 | uses: actions/setup-dotnet@v4 33 | with: 34 | dotnet-version: 9.x 35 | 36 | - name: Setup docfx 37 | run: dotnet tool update -g docfx 38 | 39 | - name: Build documentation 40 | run: docfx ./docfx/docfx.json 41 | 42 | - name: Upload documentation 43 | uses: actions/upload-pages-artifact@v3 44 | with: 45 | path: './docfx/_site' 46 | 47 | - name: Deploy to GitHub Pages 48 | id: deployment 49 | uses: actions/deploy-pages@v4 50 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | permissions: 13 | actions: read 14 | contents: read 15 | security-events: write 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup .NET 6.0 21 | uses: actions/setup-dotnet@v4 22 | with: 23 | dotnet-version: 6.x 24 | 25 | - name: Setup .NET 7.0 26 | uses: actions/setup-dotnet@v4 27 | with: 28 | dotnet-version: 7.x 29 | 30 | - name: Setup .NET 8.0 31 | uses: actions/setup-dotnet@v4 32 | with: 33 | dotnet-version: 8.x 34 | 35 | - name: Setup .NET 9.0 36 | uses: actions/setup-dotnet@v4 37 | with: 38 | dotnet-version: 9.x 39 | 40 | - name: Restore dependencies 41 | run: dotnet restore 42 | 43 | - name: Build 44 | run: dotnet build ./src/LightResults/LightResults.csproj --configuration Release --no-restore 45 | 46 | - name: Pack 47 | run: dotnet pack ./src/LightResults/LightResults.csproj --configuration Release --no-build --output . 48 | 49 | - name: Push to NuGet 50 | run: dotnet nuget push "*.nupkg" --api-key ${{secrets.NUGET_API_KEY}} --source https://api.nuget.org/v3/index.json 51 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | build: 13 | name: Test 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Initialize CodeQL 24 | uses: github/codeql-action/init@v3 25 | with: 26 | languages: 'csharp' 27 | 28 | - name: Setup .NET 6.0 29 | uses: actions/setup-dotnet@v4 30 | with: 31 | dotnet-version: 6.x 32 | 33 | - name: Setup .NET 7.0 34 | uses: actions/setup-dotnet@v4 35 | with: 36 | dotnet-version: 7.x 37 | 38 | - name: Setup .NET 8.0 39 | uses: actions/setup-dotnet@v4 40 | with: 41 | dotnet-version: 8.x 42 | 43 | - name: Setup .NET 9.0 44 | uses: actions/setup-dotnet@v4 45 | with: 46 | dotnet-version: 9.x 47 | 48 | - name: Restore dependencies 49 | run: dotnet restore 50 | 51 | - name: Build 52 | run: dotnet build --configuration Release --no-restore 53 | 54 | - name: Test 55 | run: dotnet test --configuration Release --no-build --verbosity normal --framework net8.0 56 | 57 | - name: Perform CodeQL analysis 58 | uses: github/codeql-action/analyze@v3 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | bin/ 3 | obj/ 4 | 5 | # Visual Studio files 6 | .vs 7 | 8 | # JetBrains IDE configuration 9 | .idea 10 | *.DotSettings.user 11 | 12 | # Backup files 13 | *.bak 14 | 15 | # Docs 16 | docfx/_site/** 17 | docfx/netstandard2.0/** 18 | docfx/net6.0/** 19 | docfx/net7.0/** 20 | docfx/net8.0/** 21 | -------------------------------------------------------------------------------- /AGENTS.md: -------------------------------------------------------------------------------- 1 | # Guidance for AI contributors 2 | 3 | This repository contains **LightResults**, a very small but highly-optimized .NET library that implements the Result Pattern. The code is intended to be used in performance critical paths of applications that handle millions of requests per 4 | second. The current version targets multiple frameworks (netstandard2.0 and net6.0–net9.0) and relies heavily on low allocation techniques. 5 | 6 | ## Repository layout 7 | 8 | - `src/LightResults` – Main library. 9 | - `Result.cs` / `Result\`1.cs` – immutable `readonly struct` implementations. 10 | - `Error.cs` – immutable error type. 11 | - `IResult.cs`, `IResult\`1.cs`, `IError.cs` – API contracts. 12 | - `Common/` – helper utilities and optional interfaces when targeting newer frameworks. 13 | - `tests/LightResults.Tests` – xUnit tests for the library. 14 | - `docfx/` – documentation generation config. 15 | - `LightResults.sln` – solution file. 16 | 17 | ## Coding style 18 | 19 | - C# files use 4 space indentation and braces on the next line. 20 | - Private fields are named with leading underscores (`_errors`, `_isSuccess`). 21 | - Public APIs use PascalCase. Generic type parameters use the `TValue` naming convention. 22 | - Avoid LINQ in hot paths – explicit `for` loops are used instead to reduce allocations. 23 | - The library prefers `readonly struct` and `init` properties to keep allocations to a minimum and maintain immutability. 24 | - String formatting is performance oriented (`string.Create` with pre-computed lengths) – follow existing patterns in `StringHelper.cs`. 25 | 26 | ## Performance considerations 27 | 28 | - Do not introduce allocations inside frequently executed methods. Use `readonly` collections and structs when possible. 29 | - Exceptions should not be part of normal control flow. Factory methods like `Result.Failure(Exception ex)` wrap exceptions into metadata instead of throwing. 30 | - When iterating through errors, prefer `for` loops and avoid `foreach`/LINQ which allocate enumerators. 31 | - The code is AOT compatible and compiled with optimizations enabled; keep new code trim-friendly. 32 | 33 | ## Building and testing 34 | 35 | The project uses the standard .NET SDK. When a .NET environment is available you can run: 36 | 37 | ```bash 38 | # Restore and build 39 | dotnet build LightResults.sln 40 | 41 | # Run tests 42 | dotnet test tests/LightResults.Tests/LightResults.Tests.csproj -f net9.0 43 | ``` 44 | 45 | The test project targets multiple frameworks. When `Mono` is not available (as 46 | in the Codex container) the `net481` target fails to start. Specify a supported 47 | framework like `net9.0` to run the unit tests successfully. 48 | 49 | However, building or testing is not required for simple documentation updates. This repository currently does not include an automated setup script for installing the .NET SDK. 50 | 51 | Do not restore, run, or build performance benchmarks found in `tools`. 52 | 53 | ## Adding features or fixes 54 | 55 | 1. Maintain API surface stability—public members are part of a widely used library. 56 | 2. Keep the implementation allocation free when possible and favour explicit loops and array usage. 57 | 3. Include or update unit tests under `tests/LightResults.Tests` when modifying behaviour. 58 | 4. The public documentation site is built automatically by `.github/workflows/docs.yml` from the `docfx` directory. 59 | 5. Always make sure the `README.md` and `docfx` is up to date and matches the public API. 60 | 61 | ## Useful references 62 | 63 | - `src/LightResults/Result.cs` and `src/LightResults/Result\`1.cs` show the patterns for allocating errors, success values and formatting output. 64 | - `tests/LightResults.Tests` demonstrates expected behaviour of the API and how custom error types can be implemented. 65 | 66 | -------------------------------------------------------------------------------- /Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jscarle/LightResults/38e7a43eeead69b50be88a690153645c9ec8019e/Banner.png -------------------------------------------------------------------------------- /EXPLAIN.md: -------------------------------------------------------------------------------- 1 | # LightResults API Overview 2 | 3 | LightResults implements a lightweight Result Pattern for .NET. It provides immutable types to represent success or failure states without throwing exceptions. 4 | 5 | ## Namespaces 6 | - `LightResults` – contains `Result`, `Result`, `Error`, `IResult`, `IResult` and `IError`. 7 | - `LightResults.Common` – defines `IActionableResult` and `IActionableResult`; these interfaces are only available when targeting .NET 7 or later. 8 | 9 | ## Core Types 10 | 11 | ### `Result` (readonly struct) 12 | Represents a success or failure without a value. 13 | Constructors are private or internal – no public constructors exist. Use the static factory methods below to create instances. 14 | 15 | Static factory methods returning `Result`: 16 | - `Result.Success()` – create a success instance. 17 | - `Result.Failure()` – failure with an empty error. 18 | - `Result.Failure(string message)` 19 | - `Result.Failure(string message, (string Key, object? Value) metadata)` 20 | - `Result.Failure(string message, KeyValuePair metadata)` 21 | - `Result.Failure(string message, IReadOnlyDictionary metadata)` 22 | - `Result.Failure(Exception? ex)` – derives the message from the exception and stores the exception under `"Exception"` in the resulting error. 23 | - `Result.Failure(string message, Exception? ex)` 24 | - `Result.Failure(IError error)` 25 | - `Result.Failure(IEnumerable errors)` 26 | - `Result.Failure(IReadOnlyList errors)` 27 | 28 | Static factory methods returning `Result`: 29 | - `Result.Success(TValue value)` 30 | - `Result.Failure()` 31 | - `Result.Failure(string message)` 32 | - `Result.Failure(string message, (string Key, object? Value) metadata)` 33 | - `Result.Failure(string message, KeyValuePair metadata)` 34 | - `Result.Failure(string message, IReadOnlyDictionary metadata)` 35 | - `Result.Failure(Exception? ex)` – derives the message from the exception and stores the exception under `"Exception"` in the resulting error. 36 | - `Result.Failure(string message, Exception? ex)` 37 | - `Result.Failure(IError error)` 38 | - `Result.Failure(IEnumerable errors)` 39 | - `Result.Failure(IReadOnlyList errors)` 40 | 41 | Instance members: 42 | - `bool IsSuccess()` 43 | - `bool IsFailure()` 44 | - `bool IsFailure(out IError error)` – retrieves the first error. 45 | - `bool HasError()` / `bool HasError(out TError error)` – `TError : IError` 46 | - `Result AsFailure()` – ensure a failed result preserving existing errors. 47 | - `Result AsFailure()` – convert to a different failure type. 48 | - `IReadOnlyCollection Errors` 49 | - Implicit conversion from `Error` to `Result`. 50 | - Equality: implements `IEquatable` with `Equals(in Result)` and `Equals(object?)`, provides `GetHashCode()`, and `==`/`!=` operators. Overrides `ToString()`. 51 | - Implements `IResult` (netstandard2.0–net6.0) or `IActionableResult` (net7.0+) 52 | 53 | 54 | ### `Result` (readonly struct) 55 | Represents a result carrying a value on success. 56 | Constructors are internal. Create instances using the static `Result` methods or via the implicit conversions from `TValue` and `Error`. 57 | 58 | Instance members: 59 | - `bool IsSuccess()` 60 | - `bool IsSuccess(out TValue value)` 61 | - `bool IsSuccess(out TValue value, out IError error)` 62 | - `bool IsFailure()` 63 | - `bool IsFailure(out IError error)` 64 | - `bool IsFailure(out IError error, out TValue value)` 65 | - `bool HasError()` / `bool HasError(out TError error)` – `TError : IError` 66 | - `Result AsFailure()` – ensure a failed result preserving existing errors. 67 | - `Result AsFailure()` 68 | - `IReadOnlyCollection Errors` 69 | - Implicit conversions: `TValue` → success result, `Error` → failure result. 70 | - Equality: implements `IEquatable>` with `Equals(in Result)` and `Equals(object?)`, provides `GetHashCode()`, and `==`/`!=` operators. Overrides `ToString()`. 71 | - Implements `IResult` (netstandard2.0–net6.0) or `IActionableResult>` (net7.0+) 72 | 73 | 74 | ### `Error` (class) 75 | Represents an error with an optional message and metadata. 76 | This class is not sealed, enabling custom error subclasses. 77 | 78 | Constructors: 79 | - `Error()` – empty error. 80 | - `Error(string message)` 81 | - `Error(Exception? ex)` – derives the message from the exception and stores it under `"Exception"` in `Metadata`. 82 | - `Error(string message, Exception? ex)` – when `ex` is not null, stores the exception under `"Exception"` in `Metadata`. 83 | - `Error(string message, KeyValuePair metadata)` 84 | - `Error(string message, IEnumerable> metadata)` (available on .NET 6 or later) 85 | - `Error(string message, IReadOnlyDictionary metadata)` 86 | 87 | Properties: 88 | - `string Message` 89 | - `IReadOnlyDictionary Metadata` 90 | - `Exception? Exception` – retrieved from the `Metadata` entry named "Exception" if present 91 | - `static IError Empty` 92 | 93 | Properties are init-only to keep errors immutable. 94 | Implements `IEquatable` with equality operators, `Equals(...)`, `GetHashCode()`, and overrides `ToString()`. 95 | 96 | ### Interfaces 97 | 98 | #### `IError` 99 | - `string Message { get; }` 100 | - `Exception? Exception { get; }` 101 | - `IReadOnlyDictionary Metadata { get; }` 102 | 103 | #### `IResult` 104 | - `IReadOnlyCollection Errors` 105 | - `bool IsSuccess()` 106 | - `bool IsFailure()` 107 | - `bool IsFailure(out IError error)` 108 | - `bool HasError()` – `TError : IError` 109 | - `bool HasError(out TError error)` – `TError : IError` 110 | 111 | #### `IResult` – extends `IResult` 112 | - `bool IsSuccess(out TValue value)` 113 | - `bool IsSuccess(out TValue value, out IError error)` 114 | - `bool IsFailure(out IError error, out TValue value)` 115 | 116 | #### `IActionableResult` *(NET 7+)* 117 | - extends `IResult` 118 | - generic constraint: `where TResult : IResult` 119 | - `static abstract TResult Success()` 120 | - `static abstract TResult Failure()` 121 | - `static abstract TResult Failure(string errorMessage)` 122 | - `static abstract TResult Failure(string errorMessage, (string Key, object? Value) metadata)` 123 | - `static abstract TResult Failure(string errorMessage, KeyValuePair metadata)` 124 | - `static abstract TResult Failure(string errorMessage, IReadOnlyDictionary metadata)` 125 | - `static abstract TResult Failure(Exception? ex)` 126 | - `static abstract TResult Failure(string errorMessage, Exception? ex)` 127 | - `static abstract TResult Failure(IError error)` 128 | - `static abstract TResult Failure(IEnumerable errors)` 129 | - `static abstract TResult Failure(IReadOnlyList errors)` 130 | 131 | #### `IActionableResult` *(NET 7+)* 132 | - extends `IResult` 133 | - generic constraint: `where TResult : IResult` 134 | - `static abstract TResult Success(TValue value)` 135 | - `static abstract TResult Failure()` 136 | - `static abstract TResult Failure(string errorMessage)` 137 | - `static abstract TResult Failure(string errorMessage, (string Key, object? Value) metadata)` 138 | - `static abstract TResult Failure(string errorMessage, KeyValuePair metadata)` 139 | - `static abstract TResult Failure(string errorMessage, IReadOnlyDictionary metadata)` 140 | - `static abstract TResult Failure(Exception? ex)` 141 | - `static abstract TResult Failure(string errorMessage, Exception? ex)` 142 | - `static abstract TResult Failure(IError error)` 143 | - `static abstract TResult Failure(IEnumerable errors)` 144 | - `static abstract TResult Failure(IReadOnlyList errors)` 145 | ## Usage Pattern 146 | 1. Create results using the static `Result` methods rather than constructors. 147 | 2. Check results with `IsSuccess()`/`IsFailure()` before accessing values or errors. 148 | 3. Use `HasError()` to branch on specific error types. 149 | 4. Custom errors can inherit from `Error` to represent domain-specific failures. 150 | 5. Convert failed results to another type with `AsFailure()` or `AsFailure()`. 151 | 6. Prefer returning `Result` or `Result` from methods instead of throwing exceptions. 152 | 153 | ## Target Frameworks 154 | This library targets `netstandard2.0`, `net6.0`, `net7.0`, `net8.0` and `net9.0` and is AOT-compatible. 155 | -------------------------------------------------------------------------------- /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jscarle/LightResults/38e7a43eeead69b50be88a690153645c9ec8019e/Icon.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jean-Sebastien Carle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LightResults.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightResults.Tests", "tests\LightResults.Tests\LightResults.Tests.csproj", "{93C8BC67-7F34-4935-A671-F0B12AEDB072}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightResults", "src\LightResults\LightResults.csproj", "{186EBDB5-A589-4E0C-AE40-E5141E438B58}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightResults.CurrentBenchmarks", "tools\LightResults.CurrentBenchmarks\LightResults.CurrentBenchmarks.csproj", "{7306EA93-C837-42B4-98C2-461292DC099F}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightResults.DevelopBenchmarks", "tools\LightResults.DevelopBenchmarks\LightResults.DevelopBenchmarks.csproj", "{16665976-A6A1-41BB-A8DC-6A865BF9EB3C}" 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{A9E91C2F-692F-4BE6-9637-34873C221613}" 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{A6C9FED0-42B6-488C-8961-DE13F291434B}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightResults.ComparisonBenchmarks", "tools\LightResults.ComparisonBenchmarks\LightResults.ComparisonBenchmarks.csproj", "{256BD279-B087-48FE-8A7B-E68F8D18B792}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {93C8BC67-7F34-4935-A671-F0B12AEDB072}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {93C8BC67-7F34-4935-A671-F0B12AEDB072}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {93C8BC67-7F34-4935-A671-F0B12AEDB072}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {93C8BC67-7F34-4935-A671-F0B12AEDB072}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {186EBDB5-A589-4E0C-AE40-E5141E438B58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {186EBDB5-A589-4E0C-AE40-E5141E438B58}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {186EBDB5-A589-4E0C-AE40-E5141E438B58}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {186EBDB5-A589-4E0C-AE40-E5141E438B58}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {7306EA93-C837-42B4-98C2-461292DC099F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {7306EA93-C837-42B4-98C2-461292DC099F}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {7306EA93-C837-42B4-98C2-461292DC099F}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {7306EA93-C837-42B4-98C2-461292DC099F}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {16665976-A6A1-41BB-A8DC-6A865BF9EB3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {16665976-A6A1-41BB-A8DC-6A865BF9EB3C}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {16665976-A6A1-41BB-A8DC-6A865BF9EB3C}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {16665976-A6A1-41BB-A8DC-6A865BF9EB3C}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {256BD279-B087-48FE-8A7B-E68F8D18B792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {256BD279-B087-48FE-8A7B-E68F8D18B792}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {256BD279-B087-48FE-8A7B-E68F8D18B792}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {256BD279-B087-48FE-8A7B-E68F8D18B792}.Release|Any CPU.Build.0 = Release|Any CPU 43 | EndGlobalSection 44 | GlobalSection(NestedProjects) = preSolution 45 | {7306EA93-C837-42B4-98C2-461292DC099F} = {A9E91C2F-692F-4BE6-9637-34873C221613} 46 | {16665976-A6A1-41BB-A8DC-6A865BF9EB3C} = {A9E91C2F-692F-4BE6-9637-34873C221613} 47 | {93C8BC67-7F34-4935-A671-F0B12AEDB072} = {A6C9FED0-42B6-488C-8961-DE13F291434B} 48 | {256BD279-B087-48FE-8A7B-E68F8D18B792} = {A9E91C2F-692F-4BE6-9637-34873C221613} 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /LightResults.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | False 3 | CHOP_ALWAYS 4 | True 5 | True 6 | True 7 | True -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Banner](https://raw.githubusercontent.com/jscarle/LightResults/main/Banner.png)](https://github.com/jscarle/LightResults) 2 | 3 | # LightResults - Operation Result Patterns for .NET 4 | 5 | LightResults is an extremely light and modern .NET library that provides a simple and flexible 6 | implementation of the Result Pattern. The Result Pattern is a way of representing the outcome 7 | of an operation, whether it's successful or has encountered an error, in a more explicit and 8 | structured manner. This project is heavily inspired by [Michael Altmann](https://github.com/altmann)'s 9 | excellent work with [FluentResults](https://github.com/altmann/FluentResults). 10 | 11 | [![test](https://img.shields.io/github/actions/workflow/status/jscarle/LightResults/test.yml?logo=github)](https://github.com/jscarle/LightResults) 12 | [![nuget](https://img.shields.io/nuget/v/LightResults)](https://www.nuget.org/packages/LightResults) 13 | [![downloads](https://img.shields.io/nuget/dt/LightResults)](https://www.nuget.org/packages/LightResults) 14 | 15 | ## References 16 | 17 | This library targets .NET Standard 2.0, .NET 6.0, .NET 7.0, .NET 8.0, and .NET 9.0. 18 | 19 | ## Dependencies 20 | 21 | This library has no dependencies. 22 | 23 | ## Advantages of this library 24 | 25 | - 🪶 Lightweight — Only contains what's necessary to implement the Result Pattern. 26 | - ⚙️ Extensible — Simple interfaces and base classes make it easy to adapt. 27 | - 🧱 Immutable — Results and errors are immutable and cannot be changed after being created. 28 | - 🧵 Thread-safe — Error and metadata collections are read-only. 29 | - ✨ Modern — Built against the latest version of .NET using the most recent best practices. 30 | - 🧪 Native — Written, compiled, and tested against the latest versions of .NET. 31 | - ❤️ Compatible — Available for dozens of versions of .NET as a 32 | [.NET Standard 2.0](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0) library. 33 | - 🪚 Trimmable — Compatible with [ahead-of-time compilation](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) (AOT) as of .NET 7.0. 34 | - 🚀 Performant — Heavily optimized and [benchmarked](https://jscarle.github.io/LightResults/docs/performance.html) to aim for the highest possible performance. 35 | 36 | ## Extensions 37 | 38 | Several [extensions are available](https://github.com/jscarle/LightResults.Extensions) to simplify implementation that use LightResults. 39 | 40 | ## Documentation 41 | 42 | Make sure to [read the docs](https://jscarle.github.io/LightResults/) for the full API. 43 | 44 | ## Getting Started 45 | 46 | LightResults consists of only three types: `Result`, `Result`, and `Error`. 47 | 48 | - The `Result` class represents a generic result indicating success or failure. 49 | - The `Result` class represents a success or failure result with a value. 50 | - The `Error` class represents an error with a message and optional associated metadata. 51 | 52 | ### Creating a successful result 53 | 54 | Successful results can be created using the `Success` method. 55 | 56 | ```csharp 57 | var successResult = Result.Success(); 58 | 59 | var successResultWithValue = Result.Success(349.4); 60 | ``` 61 | 62 | ### Creating a failure result 63 | 64 | Failed results can be created using the `Failure` method. 65 | 66 | ```csharp 67 | var failureResult = Result.Failure(); 68 | 69 | var failureResultWithMessage = Result.Failure("Operation failure!"); 70 | 71 | var failureResultWithMessageAndMetadata = Result.Failure("Operation failure!", ("UserId", userId)); 72 | 73 | var failureResultWithMessageAndException = Result.Failure("Operation failure!", ex); 74 | ``` 75 | 76 | ### Checking the state of a result 77 | 78 | There are two methods used to check a result, `IsSuccess()` and `IsFailure()`. Both of which have several overloads to obtain the 79 | value and error. 80 | 81 | ```csharp 82 | if (result.IsSuccess()) 83 | { 84 | // The result is successful. 85 | } 86 | 87 | if (result.IsFailure(out var error)) 88 | { 89 | // The result is failure. 90 | if (error.Message.Length > 0) 91 | Console.WriteLine(error.Message); 92 | else 93 | Console.WriteLine("An unknown error occurred!"); 94 | } 95 | ``` 96 | 97 | ### Getting the value 98 | 99 | The value from a successful result can be retrieved through the `out` parameter of the `IsSuccess()` method. 100 | 101 | ```csharp 102 | if (result.IsSuccess(out var value)) 103 | { 104 | Console.WriteLine($"Value is {value}"); 105 | } 106 | ``` 107 | 108 | ### Converting failed results 109 | 110 | A failed result can be converted to another result type using `AsFailure`. 111 | 112 | ```csharp 113 | var result = Result.Failure("Invalid input"); 114 | var typed = result.AsFailure(); 115 | var backToNonGeneric = typed.AsFailure(); 116 | ``` 117 | 118 | 119 | ### Creating errors 120 | 121 | Errors can be created with or without a message. 122 | 123 | ```csharp 124 | var emptyError = Error.Empty; 125 | 126 | var errorWithoutMessage = new Error(); 127 | 128 | var errorWithMessage = new Error("Something went wrong!"); 129 | ``` 130 | 131 | Or with a message and metadata. 132 | 133 | ```csharp 134 | var errorWithMetadataTuple = new Error("Something went wrong!", ("Key", "Value")); 135 | 136 | var metadata = new Dictionary { { "Key", "Value" } }; 137 | var errorWithMetadataDictionary = new Error("Something went wrong!", metadata); 138 | 139 | var errorWithMetadataKeyValuePair = new Error("Something went wrong!", new KeyValuePair("Key", "Value")); 140 | 141 | var errorWithMetadataEnumerable = new Error("Something went wrong!", new[] { new KeyValuePair("Key", "Value") }); 142 | 143 | var ex = new InvalidOperationException(); 144 | var errorWithException = new Error(ex); 145 | 146 | var errorWithMessageAndException = new Error("Something went wrong!", ex); 147 | ``` 148 | 149 | ### Accessing the Exception property 150 | 151 | When an error contains an exception, it can be accessed through the `Exception` property available on both `Error` and `IError`. 152 | 153 | ```csharp 154 | var ex = new InvalidOperationException("Something went wrong!"); 155 | var error = new Error(ex); 156 | 157 | if (error.Exception != null) 158 | { 159 | Console.WriteLine($"Exception: {error.Exception.Message}"); 160 | } 161 | 162 | // Also works with IError interface 163 | IError interfaceError = error; 164 | if (interfaceError.Exception != null) 165 | { 166 | Console.WriteLine($"Exception: {interfaceError.Exception.Message}"); 167 | } 168 | ``` 169 | 170 | ### Custom errors 171 | 172 | The best way to represent specific errors is to make custom error classes that inherit from `Error` 173 | and define the error message as a base constructor parameter. 174 | 175 | ```csharp 176 | public sealed class NotFoundError : Error 177 | { 178 | public NotFoundError() 179 | : base("The resource cannot be found.") 180 | { 181 | } 182 | } 183 | 184 | var notFoundError = new NotFoundError(); 185 | var notFoundResult = Result.Failure(notFoundError); 186 | ``` 187 | 188 | Then the result can be checked against that error type. 189 | 190 | ```csharp 191 | if (result.IsFailure(out var error) && error is NotFoundError) 192 | { 193 | // Looks like the resource was not found, we better do something about that! 194 | } 195 | ``` 196 | 197 | Or checked to see if there are any errors of that type. 198 | 199 | ```csharp 200 | if (result.IsFailure() && result.HasError()) 201 | { 202 | // At least one of the errors was a NotFoundError. 203 | } 204 | ``` 205 | 206 | This can be especially useful when combined with metadata that is related to a specific type of error. 207 | 208 | ```csharp 209 | public sealed class HttpError : Error 210 | { 211 | public HttpError(HttpStatusCode statusCode) 212 | : base("An HTTP error occurred.", ("StatusCode", statusCode)) 213 | { 214 | } 215 | } 216 | ``` 217 | 218 | We can further simplify creating errors by creating an error factory. 219 | 220 | ```csharp 221 | public static class AppError 222 | { 223 | public static Result NotFound() 224 | { 225 | var notFoundError = new NotFoundError(); 226 | return Result.Failure(notFoundError); 227 | } 228 | 229 | public static Result HttpError(HttpStatusCode statusCode) 230 | { 231 | var httpError = new HttpError(statusCode); 232 | return Result.Failure(httpError); 233 | } 234 | } 235 | ``` 236 | 237 | Which clearly and explicitly describes the results. 238 | 239 | ```csharp 240 | public Result GetPerson(int id) 241 | { 242 | var person = _database.GetPerson(id); 243 | 244 | if (person is null) 245 | return AppError.NotFound(); 246 | 247 | return Result.Success(); 248 | } 249 | ``` 250 | 251 | ### Comparing errors 252 | 253 | `Error` implements `IEquatable` so instances with the same message and metadata are considered equal. 254 | 255 | ```csharp 256 | var error1 = new Error("Invalid", ("Code", 42)); 257 | var error2 = new Error("Invalid", ("Code", 42)); 258 | 259 | if (error1 == error2) 260 | { 261 | // Errors are equal 262 | } 263 | ``` 264 | 265 | ### Handling Exceptions 266 | 267 | Specific overloads have been added to `Failure()` and `Failure()` to simplify using try-catch blocks and return from them with a result instead of 268 | throwing. 269 | 270 | ```csharp 271 | public Result DoSomeWork() 272 | { 273 | try 274 | { 275 | // We must not throw an exception in this method! 276 | } 277 | catch(Exception ex) 278 | { 279 | return Result.Failure(ex); 280 | } 281 | 282 | return Result.Success(); 283 | } 284 | ``` 285 | 286 | ### Static abstract members in interfaces 287 | 288 | _Note: Applies to .NET 7.0 (C# 11.0) or higher only!_ 289 | 290 | Thanks to the 291 | [static abstract members in interfaces](https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/static-abstract-interface-methods) 292 | introduced in .NET 7.0 (C# 11.0), it is possible to use generics to obtain access to the methods 293 | of the generic variant of the result. As such the error factory can be enhanced to take advantage of that. 294 | 295 | ```csharp 296 | public static class AppError 297 | { 298 | public static Result NotFound() 299 | { 300 | var notFoundError = new NotFoundError(); 301 | return Result.Failure(notFoundError); 302 | } 303 | 304 | public static TResult NotFound() 305 | { 306 | var notFoundError = new NotFoundError(); 307 | return TResult.Failure(notFoundError); 308 | } 309 | } 310 | ``` 311 | 312 | ## What's new in v9.0 313 | 314 | The API for LightResults was completely redesigned for v9.0 to improve performance and remove any potential pits of failure caused by the prior version's use 315 | of properties that could result in exceptions being thrown when values were accessed without checking the state of the result. Thus, there are several breaking 316 | changes, detailed below, that developers must be aware of when upgrading from v8.0 to v9.0. 317 | 318 | ### Breaking changes between v8.0 and v9.0 319 | 320 | - Factory methods for creating generic results have changed names. 321 | - `Result.Ok()` has been renamed to `Result.Success()`. 322 | - `Result.Fail()` has been renamed to `Result.Failure()`. 323 | - Factory methods for creating results with values have changed names and type to allow omitting the type when it is known. 324 | - `Result.Ok()` has been renamed and moved to `Result.Success()`. 325 | - `Result.Fail()` has been renamed to `Result.Failure()`. 326 | - The `Value` and `Error` properties have been removed. 327 | - `result.Value` has been replaced by `result.IsSuccess(out var value)`. 328 | - `result.Error` has been replaced by `result.IsFailure(out var error)`. 329 | - Several constructors of the `Error` type have been removed or have changed. 330 | - `Error((string Key, object Value) metadata)` has been removed. 331 | - `Error(IDictionary metadata)` has been removed. 332 | - `Error(string message, IDictionary metadata)` has been changed to 333 | `Error(string message, IReadOnlyDictionary metadata)`. 334 | 335 | ### Migrating 336 | 337 | The following steps in the following order will reduce the amount of manual work required to migrate and refactor code to use the new API. 338 | 339 | 1. Find and replace all instances of `Result.Ok(` with `Result.Success(`. 340 | 2. Find and replace all instances of `Result.Fail(` with `Result.Failure(`. 341 | 3. Regex find and replace all instances of `Result(<[^>]+>)\.Ok\(` with `Result.Success$1(`. 342 | 4. Regex find and replace all instances of `Result(<[^>]+>)\.Fail\(` with `Result.Failure$1(`. 343 | 5. Find and replace all instances of `.IsSuccess` with `IsSuccess(out var value)`. 344 | 6. Find and replace all instances of `.IsFailed` with `IsFailure(out var error)`. 345 | 7. Find instances of `result.Value` and refactor them to the use the `value` exposed by the `IsSuccess()` method. 346 | 8. Find instances of `result.Error` and refactor them to the use the `error` exposed by the `IsFailure()` method. 347 | 348 | ### New method overloads and property initializers 349 | 350 | - New overloads have been added for `KeyValuePair` metadata. 351 | - `Result.Failure(string errorMessage, KeyValuePair metadata)` has been added. 352 | - `Result.Failure(string errorMessage, KeyValuePair metadata)` has been added. 353 | - New overloads have been added to simplify handling exceptions. 354 | - `Result.Failure(Exception ex)` has been added. 355 | - `Result.Failure(string errorMessage, Exception ex)` has been added. 356 | - `Result.Failure(Exception ex)` has been added. 357 | - `Result.Failure(string errorMessage, Exception ex)` has been added. 358 | - New overloads were added to access the value. 359 | - `result.IsSuccess(out TValue value)` has been added. 360 | - `result.IsFailure(out IError error, out TValue value)` has been added. 361 | - New overloads were added to access the first error. 362 | - `result.IsFailure(out IError error)` has been added. 363 | - `result.IsSuccess(out TValue value, out IError error)` has been added. 364 | - `result.HasError(out TError error)` has been added. 365 | - New property initializers were added to `Error`. 366 | - `Message { get; }` has changed to `Message { get; init; }`. 367 | - `Metadata { get; }` has changed to `Metadata { get; init; }`. 368 | - `Error(Exception exception)` has been added. 369 | - `Error(string message, Exception exception)` has been added. 370 | - `Error.Empty` is now publicly accessible. 371 | - `Exception { get; }` has been added to both `Error` and `IError`. 372 | - New helper methods were added to convert failed results. 373 | - `result.AsFailure()` and `result.AsFailure()` convert an existing result into a failure result of another type. 374 | - Additional `Error` constructors were introduced for metadata collections. 375 | - `Error(string message, KeyValuePair metadata)` has been added. 376 | - `Error(string message, IEnumerable> metadata)` has been added. 377 | -------------------------------------------------------------------------------- /docfx/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "../src", 7 | "files": [ 8 | "**/*.csproj" 9 | ] 10 | } 11 | ], 12 | "outputFormat": "apiPage", 13 | "dest": "netstandard2.0", 14 | "properties": { 15 | "TargetFramework": "netstandard2.0" 16 | } 17 | }, 18 | { 19 | "src": [ 20 | { 21 | "src": "../src", 22 | "files": [ 23 | "**/*.csproj" 24 | ] 25 | } 26 | ], 27 | "outputFormat": "apiPage", 28 | "dest": "net6.0", 29 | "properties": { 30 | "TargetFramework": "net6.0" 31 | } 32 | }, 33 | { 34 | "src": [ 35 | { 36 | "src": "../src", 37 | "files": [ 38 | "**/*.csproj" 39 | ] 40 | } 41 | ], 42 | "outputFormat": "apiPage", 43 | "dest": "net7.0", 44 | "properties": { 45 | "TargetFramework": "net7.0" 46 | } 47 | }, 48 | { 49 | "src": [ 50 | { 51 | "src": "../src", 52 | "files": [ 53 | "**/*.csproj" 54 | ] 55 | } 56 | ], 57 | "outputFormat": "apiPage", 58 | "dest": "net8.0", 59 | "properties": { 60 | "TargetFramework": "net8.0" 61 | } 62 | }, 63 | { 64 | "src": [ 65 | { 66 | "src": "../src", 67 | "files": [ 68 | "**/*.csproj" 69 | ] 70 | } 71 | ], 72 | "outputFormat": "apiPage", 73 | "dest": "net9.0", 74 | "properties": { 75 | "TargetFramework": "net9.0" 76 | } 77 | } 78 | ], 79 | "build": { 80 | "content": [ 81 | { 82 | "files": "**/*.{md,yml}", 83 | "exclude": [ 84 | "_site/**" 85 | ] 86 | } 87 | ], 88 | "resource": [ 89 | { 90 | "files": [ 91 | "images/**", 92 | "**/googlec98a83d0d9926e87.html" 93 | ] 94 | } 95 | ], 96 | "output": "_site", 97 | "template": [ 98 | "default", 99 | "modern" 100 | ], 101 | "globalMetadata": { 102 | "_appName": "LightResults", 103 | "_appTitle": "LightResults", 104 | "_appLogoPath": "images/Logo.png", 105 | "_appFaviconPath": "images/Icon.png", 106 | "_appFooter": "Developed and maintained by Jean-Sebastien Carle — jscarle.dev\r\n", 107 | "_enableSearch": true 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /docfx/docs/breaking-changes.md: -------------------------------------------------------------------------------- 1 | # What's new in v9.0 2 | 3 | The API for LightResults was completely redesigned for v9.0 to improve performance and remove any potential pits of failure caused by the prior version's use 4 | of properties that could result in exceptions being thrown when values were accessed without checking the state of the result. Thus, there are several breaking 5 | changes, detailed below, that developers must be aware of when upgrading from v8.0 to v9.0. 6 | 7 | ### Breaking changes between v8.0 and v9.0 8 | 9 | - Factory methods for creating generic results have changed names. 10 | - `Result.Ok()` has been renamed to `Result.Success()`. 11 | - `Result.Fail()` has been renamed to `Result.Failure()`. 12 | - Factory methods for creating results with values have changed names and type to allow omitting the type when it is known. 13 | - `Result.Ok()` has been renamed and moved to `Result.Success()`. 14 | - `Result.Fail()` has been renamed to `Result.Failure()`. 15 | - The `Value` and `Error` properties have been removed. 16 | - `result.Value` has been replaced by `result.IsSuccess(out var value)`. 17 | - `result.Error` has been replaced by `result.IsError(out var error)`. 18 | - Several constructors of the `Error` type have been removed or have changed. 19 | - `Error((string Key, object Value) metadata)` has been removed. 20 | - `Error(IDictionary metadata)` has been removed. 21 | - `Error(string message, IDictionary metadata)` has been changed to 22 | `Error(string message, IReadOnlyDictionary metadata)`. 23 | 24 | ### Migrating 25 | 26 | The following steps in the following order will reduce the amount of manual work required to migrate and refactor code to use the new API. 27 | 28 | 1. Find and replace all instances of `Result.Ok(` with `Result.Success(`. 29 | 2. Find and replace all instances of `Result.Fail(` with `Result.Failure(`. 30 | 3. Regex find and replace all instances of `Result(<[^>]+>)\.Ok\(` with `Result.Success$1(`. 31 | 4. Regex find and replace all instances of `Result(<[^>]+>)\.Fail\(` with `Result.Failure$1(`. 32 | 5. Find and replace all instances of `.IsSuccess` with `IsSuccess(out var value)`. 33 | 6. Find and replace all instances of `.IsFailed` with `IsFailure(out var error)`. 34 | 7. Find instances of `result.Value` and refactor them to the use the `value` exposed by the `IsSuccess()` method. 35 | 8. Find instances of `result.Error` and refactor them to the use the `error` exposed by the `IsFailure()` method. 36 | 37 | ### New method overloads and property initializers 38 | 39 | - New overloads have been added for `KeyValuePair` metadata. 40 | - `Result.Failure(string errorMessage, KeyValuePair metadata)` has been added. 41 | - `Result.Failure(string errorMessage, KeyValuePair metadata)` has been added. 42 | - New overloads have been added to simplify handling exceptions. 43 | - `Result.Failure(Exception ex)` has been added. 44 | - `Result.Failure(string errorMessage, Exception ex)` has been added. 45 | - `Result.Failure(Exception ex)` has been added. 46 | - `Result.Failure(string errorMessage, Exception ex)` has been added. 47 | - New overloads where added to access the value. 48 | - `result.IsSuccess(out TValue value)` has been added. 49 | - `result.IsFailure(out IError error, out TValue value)` has been added. 50 | - New overloads where added to access the first error. 51 | - `result.IsFailure(out IError error)` has been added. 52 | - `result.IsSuccess(out TValue value, out IError error)` has been added. 53 | - `result.HasError(out TError error)` has been added. 54 | - New property initializers where added to `Error`. 55 | - `Message { get; }` has changed to `Message { get; init; }`. 56 | - `Metadata { get; }` has changed to `Metadata { get; init; }`. 57 | - `Error(Exception exception)` has been added. 58 | - `Error(string message, Exception exception)` has been added. 59 | - `Error.Empty` is now publicly accessible. 60 | - `Exception { get; }` has been added to both `Error` and `IError`. 61 | - New helper methods were added to convert failed results. 62 | - `result.AsFailure()` and `result.AsFailure()` convert an existing result into a failure result of another type. 63 | - Additional `Error` constructors were introduced for metadata collections. 64 | - `Error(string message, KeyValuePair metadata)` has been added. 65 | - `Error(string message, IEnumerable> metadata)` has been added. 66 | -------------------------------------------------------------------------------- /docfx/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | LightResults consists of only three classes `Result`, `Result`, and `Error`. 4 | 5 | - The `Result` class represents a generic result indicating success or failure. 6 | - The `Result` class represents a success or failure result with a value. 7 | - The `Error` class represents an error with a message and optional associated metadata. 8 | 9 | ### Creating a successful result 10 | 11 | Successful results can be created using the `Success` method. 12 | 13 | ```csharp 14 | var successResult = Result.Success(); 15 | 16 | var successResultWithValue = Result.Success(349.4); 17 | ``` 18 | 19 | ### Creating a failure result 20 | 21 | Failed results can be created using the `Failure` method. 22 | 23 | ```csharp 24 | var failureResult = Result.Failure(); 25 | 26 | var failureResultWithMessage = Result.Failure("Operation failure!"); 27 | 28 | var failureResultWithMessageAndMetadata = Result.Failure("Operation failure!", ("UserId", userId)); 29 | 30 | var failureResultWithMessageAndException = Result.Failure("Operation failure!", ex); 31 | ``` 32 | 33 | ### Checking the state of a result 34 | 35 | There are two methods used to check a result, `IsSuccess()` and `IsFailed()`. Both of which have several overloads to obtain the 36 | value and error. 37 | 38 | ```csharp 39 | if (result.IsSuccess()) 40 | { 41 | // The result is successful. 42 | } 43 | 44 | if (result.IsFailure(out var error)) 45 | { 46 | // The result is failure. 47 | if (error.Message.Length > 0) 48 | Console.WriteLine(error.Message); 49 | else 50 | Console.WriteLine("An unknown error occurred!"); 51 | } 52 | ``` 53 | 54 | ### Getting the value 55 | 56 | The value from a successful result can be retrieved through the `out` parameter of the `Success()` method. 57 | 58 | ```csharp 59 | if (result.IsSuccess(out var value)) 60 | { 61 | Console.WriteLine($"Value is {value}"); 62 | } 63 | ``` 64 | 65 | ### Converting failed results 66 | 67 | A failed result can be converted to another result type using `AsFailure`. 68 | 69 | ```csharp 70 | var result = Result.Failure("Invalid input"); 71 | var typed = result.AsFailure(); 72 | var backToNonGeneric = typed.AsFailure(); 73 | ``` 74 | 75 | 76 | ### Creating errors 77 | 78 | Errors can be created with or without a message. 79 | 80 | ```csharp 81 | var errorWithoutMessage = new Error(); 82 | 83 | var errorWithMessage = new Error("Something went wrong!"); 84 | ``` 85 | 86 | Or with a message and metadata. 87 | 88 | ```csharp 89 | var errorWithMetadataTuple = new Error("Something went wrong!", ("Key", "Value")); 90 | 91 | var metadata = new Dictionary { { "Key", "Value" } }; 92 | var errorWithMetadataDictionary = new Error("Something went wrong!", metadata); 93 | 94 | var errorWithMetadataKeyValuePair = new Error("Something went wrong!", new KeyValuePair("Key", "Value")); 95 | 96 | var errorWithMetadataEnumerable = new Error("Something went wrong!", new[] { new KeyValuePair("Key", "Value") }); 97 | 98 | var ex = new InvalidOperationException(); 99 | var errorWithException = new Error(ex); 100 | 101 | var errorWithMessageAndException = new Error("Something went wrong!", ex); 102 | ``` 103 | 104 | ### Accessing the Exception property 105 | 106 | When an error contains an exception, it can be accessed through the `Exception` property available on both `Error` and `IError`. 107 | 108 | ```csharp 109 | var ex = new InvalidOperationException("Something went wrong!"); 110 | var error = new Error(ex); 111 | 112 | if (error.Exception != null) 113 | { 114 | Console.WriteLine($"Exception: {error.Exception.Message}"); 115 | } 116 | 117 | // Also works with IError interface 118 | IError interfaceError = error; 119 | if (interfaceError.Exception != null) 120 | { 121 | Console.WriteLine($"Exception: {interfaceError.Exception.Message}"); 122 | } 123 | ``` 124 | 125 | ### Custom errors 126 | 127 | The best way to represent specific errors is to make custom error classes that inherit from `Error` 128 | and define the error message as a base constructor parameter. 129 | 130 | ```csharp 131 | public sealed class NotFoundError : Error 132 | { 133 | public NotFoundError() 134 | : base("The resource cannot be found.") 135 | { 136 | } 137 | } 138 | 139 | var notFoundError = new NotFoundError(); 140 | var notFoundResult = Result.Failure(notFoundError); 141 | ``` 142 | 143 | Then the result can be checked against that error type. 144 | 145 | ```csharp 146 | if (result.IsFailure(out var error) && error is NotFoundError) 147 | { 148 | // Looks like the resource was not found, we better do something about that! 149 | } 150 | ``` 151 | 152 | Or checked to see if there are any errors of that type. 153 | 154 | ```csharp 155 | if (result.IsFailure() && result.HasError()) 156 | { 157 | // At least one of the errors was a NotFoundError. 158 | } 159 | ``` 160 | 161 | This can be especially useful when combined with metadata that is related to a specific type of error. 162 | 163 | ```csharp 164 | public sealed class HttpError : Error 165 | { 166 | public HttpError(HttpStatusCode statusCode) 167 | : base("An HTTP error occurred.", ("StatusCode", statusCode)) 168 | { 169 | } 170 | } 171 | ``` 172 | 173 | We can further simplify creating errors by creating an error factory. 174 | 175 | ```csharp 176 | public static AppError 177 | { 178 | public Result NotFound() 179 | { 180 | var notFoundError = new NotFoundError(); 181 | return Result.Failure(notFoundError); 182 | } 183 | 184 | public Result HttpError(HttpStatusCode statusCode) 185 | { 186 | var httpError = new HttpError(statusCode) 187 | return Result.Failure(httpError); 188 | } 189 | } 190 | ``` 191 | 192 | Which clearly and explicitly describes the results. 193 | 194 | ```csharp 195 | public Result GetPerson(int id) 196 | { 197 | var person = _database.GetPerson(id); 198 | 199 | if (person is null) 200 | return AppError.NotFound(); 201 | 202 | return Result.Success(); 203 | } 204 | ``` 205 | 206 | ### Handling Exceptions 207 | 208 | Specific overloads have been added to `Failure()` and `Failure()` to simplify using try-catch blocks and return from them with a result instead of throwing. 209 | 210 | ```csharp 211 | public Result DoSomeWork() 212 | { 213 | try 214 | { 215 | // We must not throw an exception in this method! 216 | } 217 | catch(Exception ex) 218 | { 219 | return Result.Failure(ex); 220 | } 221 | 222 | return Result.Success(); 223 | } 224 | ``` 225 | ### Static abstract members in interfaces 226 | 227 | _Note: Applies to .NET 7.0 (C# 11.0) or higher only!_ 228 | 229 | Thanks to the 230 | [static abstract members in interfaces](https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/static-abstract-interface-methods) 231 | introduced in .NET 7.0 (C# 11.0), it is possible to use generics to obtain access to the methods 232 | of the generic variant of the result. As such the error factory can be enhanced to take advantage of that. 233 | 234 | ```csharp 235 | public static AppError 236 | { 237 | public Result NotFound() 238 | { 239 | var notFoundError = new NotFoundError(); 240 | return Result.Failure(notFoundError); 241 | } 242 | 243 | public TResult NotFound() 244 | { 245 | var notFoundError = new NotFoundError(); 246 | return TResult.Failure(notFoundError); 247 | } 248 | } 249 | ``` 250 | -------------------------------------------------------------------------------- /docfx/docs/performance.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | This library prioritizes performance as a core design principle. While it doesn't aim to replace feature-rich libraries like 4 | [Michael Altmann](https://github.com/altmann)'s excellent [FluentResults](https://github.com/altmann/FluentResults) or 5 | [Steve Smith](https://github.com/ardalis)'s popular [Ardalis.Result](https://github.com/ardalis/result). LightResults strives 6 | for exceptional performance by intentionally simplifying its API. 7 | 8 | ## Comparison 9 | 10 | Below are comparisons of LightResults against other result pattern implementations. 11 | 12 | ``` 13 | BenchmarkDotNet v0.15.2, Windows 11 (10.0.26100.4652/24H2/2024Update/HudsonValley) 14 | 13th Gen Intel Core i7-13700KF 3.40GHz, 1 CPU, 24 logical and 16 physical cores 15 | .NET SDK 9.0.303 16 | [Host] : .NET 9.0.7 (9.0.725.31616), X64 RyuJIT AVX2 17 | .NET 9.0 : .NET 9.0.7 (9.0.725.31616), X64 RyuJIT AVX2 18 | 19 | Job=.NET 9.0 Runtime=.NET 9.0 IterationTime=250ms 20 | Iterations=10 21 | ``` 22 | These results were produced using **LightResults 9.0.5** and **BenchmarkDotNet 0.15.2**. 23 | The comparison implementations used the following package versions: 24 | 25 | - **FluentResults** 4.0.0 26 | - **Ardalis.Result** 10.1.0 27 | ### Returning results 28 | 29 | #### Returning a successful result 30 | | Method | Mean | Ratio | Allocated | 31 | |-----------------------------------|----------:|------:|----------:| 32 | | LightResults: `Result.Success()` | 2.382 ns | 1.00 | - | 33 | | FluentResults: `Result.Ok()` | 46.564 ns | 19.55 | 560 B | 34 | | ArdalisResult: `Result.Success()` | 42.761 ns | 17.96 | 720 B | 35 | 36 | #### Returning a successful value result 37 | | Method | Mean | Ratio | Allocated | 38 | |-------------------------------------------|-----------:|------:|----------:| 39 | | LightResults: `Result.Success(value)` | 2.308 ns | 1.00 | - | 40 | | FluentResults: `Result.Ok(value)` | 148.860 ns | 64.50 | 1120 B | 41 | | ArdalisResult: `Result.Success(value)` | 41.865 ns | 18.14 | 640 B | 42 | 43 | #### Returning a failed result 44 | | Method | Mean | Ratio | Allocated | 45 | |----------------------------------|-----------:|-------:|----------:| 46 | | LightResults: `Result.Failure()` | 2.099 ns | 1.00 | - | 47 | | FluentResults: `Result.Fail("")` | 282.372 ns | 134.55 | 2640 B | 48 | | ArdalisResult: `Result.Error()` | 47.295 ns | 22.54 | 720 B | 49 | 50 | #### Returning a failed result with an error message 51 | | Method | Mean | Ratio | Allocated | Alloc Ratio | 52 | |----------------------------------------------|-----------:|------:|----------:|------------:| 53 | | LightResults: `Result.Failure(errorMessage)` | 21.614 ns | 1.00 | 240 B | 1.00 | 54 | | FluentResults: `Result.Fail(errorMessage)` | 120.913 ns | 5.60 | 1120 B | 4.67 | 55 | | ArdalisResult: `Result.Error(errorMessage)` | 64.953 ns | 3.01 | 1040 B | 4.33 | 56 | 57 | #### Returning a failed value result 58 | | Method | Mean | Ratio | Allocated | 59 | |-------------------------------------|-----------:|-------:|----------:| 60 | | LightResults: `Result.Failure()` | 2.500 ns | 1.00 | - | 61 | | FluentResults: `Result.Fail("")` | 285.110 ns | 114.06 | 2720 B | 62 | | ArdalisResult: `Result.Error()` | 53.565 ns | 21.43 | 640 B | 63 | 64 | #### Returning a failed value result with an error message 65 | | Method | Mean | Ratio | Allocated | Alloc Ratio | 66 | |-------------------------------------------------|-----------:|------:|----------:|------------:| 67 | | LightResults: `Result.Failure(errorMessage)` | 18.487 ns | 1.00 | 240 B | 1.00 | 68 | | FluentResults: `Result.Fail(errorMessage)` | 122.375 ns | 6.62 | 1200 B | 5.00 | 69 | | ArdalisResult: `Result.Error(errorMessage)` | 61.796 ns | 3.34 | 960 B | 4.00 | 70 | 71 | ### Checking results 72 | 73 | #### Determining if a result is successful 74 | | Method | Mean | Ratio | Allocated | 75 | |------------------------------------|-----------:|------:|----------:| 76 | | LightResults: `result.IsSuccess()` | 2.219 ns | 1.00 | - | 77 | | FluentResults: `result.IsSuccess` | 102.670 ns | 46.29 | 480 B | 78 | | ArdalisResult: `result.IsSuccess` | 2.383 ns | 1.07 | - | 79 | 80 | #### Retrieving the value 81 | | Method | Mean | Ratio | Allocated | 82 | |-------------------------------------------------|----------:|------:|----------:| 83 | | LightResults: `result.IsSuccess(out var value)` | 2.238 ns | 1.00 | - | 84 | | FluentResults: `result.Value` | 95.136 ns | 42.52 | 480 B | 85 | | ArdalisResult: `result.Value` | 2.240 ns | 1.00 | - | 86 | 87 | #### Determining if a result is failed 88 | | Method | Mean | Ratio | Allocated | 89 | |------------------------------------|-----------:|------:|----------:| 90 | | LightResults: `result.IsFailure()` | 2.196 ns | 1.00 | - | 91 | | FluentResults: `result.IsFailed` | 148.292 ns | 67.55 | 880 B | 92 | | ArdalisResult: `!result.IsSuccess` | 2.334 ns | 1.06 | - | 93 | 94 | #### Determining if a result contains a specific error 95 | | Method | Mean | Ratio | Allocated | 96 | |---------------------------------------------------------------------------------------|-----------:|------:|----------:| 97 | | LightResults: `result.HasError()` | 12.887 ns | 1.00 | - | 98 | | FluentResults: `result.HasError()` | 789.100 ns | 61.23 | 3840 B | 99 | | ArdalisResult: `result.Errors.Any(errorMessage => errorMessage.Equals(ErrorMessage))` | 20.733 ns | 1.61 | - | 100 | 101 | #### Retrieving the first error 102 | | Method | Mean | Ratio | Allocated | 103 | |----------------------------------------|-----------:|-------:|----------:| 104 | | LightResults: `result.Error` | 3.817 ns | 1.00 | - | 105 | | FluentResults: `result.Errors[0]` | 325.418 ns | 85.25 | 1760 B | 106 | | ArdalisResult: `result.Errors.First()` | 94.840 ns | 24.85 | - | 107 | 108 | ### Getting results as strings 109 | 110 | #### String representation of a successful result 111 | | Method | Mean | Ratio | Allocated | 112 | |----------------------------------------------|-----------:|-------:|----------:| 113 | | LightResults: `Result.Success().ToString()` | 2.595 ns | 1.00 | - | 114 | | FluentResults: `Result.Ok().ToString()` | 329.141 ns | 126.82 | 1200 B | 115 | | ArdalisResult: `Result.Success().ToString()` | 14.724 ns | 5.67 | - | 116 | 117 | #### String representation of a successful value result 118 | | Method | Mean | Ratio | Allocated | Alloc Ratio | 119 | |------------------------------------------------------|-----------:|------:|----------:|------------:| 120 | | LightResults: `Result.Success(value).ToString()` | 98.429 ns | 1.00 | 1040 B | 1.00 | 121 | | FluentResults: `Result.Ok(value).ToString()` | 557.393 ns | 5.66 | 2800 B | 2.69 | 122 | | ArdalisResult: `Result.Success(value).ToString()` | 14.593 ns | 0.15 | - | 0.00 | 123 | 124 | #### String representation of a failed result 125 | | Method | Mean | Ratio | Allocated | 126 | |---------------------------------------------|-----------:|-------:|----------:| 127 | | LightResults: `Result.Failure().ToString()` | 5.324 ns | 1.00 | - | 128 | | FluentResults: `Result.Fail("").ToString()` | 957.111 ns | 179.76 | 3600 B | 129 | | ArdalisResult: `Result.Error().ToString()` | 15.269 ns | 2.87 | - | 130 | 131 | #### String representation of a failed result with an error message 132 | | Method | Mean | Ratio | Allocated | Alloc Ratio | 133 | |---------------------------------------------------------|-------------:|------:|----------:|------------:| 134 | | LightResults: `Result.Failure(errorMessage).ToString()` | 108.405 ns | 1.00 | 1600 B | 1.00 | 135 | | FluentResults: `Result.Fail(errorMessage).ToString()` | 1,417.229 ns | 13.08 | 9120 B | 5.70 | 136 | | ArdalisResult: `Result.Error(errorMessage).ToString()` | 14.672 ns | 0.14 | - | 0.00 | 137 | 138 | #### String representation of a failed value result 139 | | Method | Mean | Ratio | Allocated | 140 | |------------------------------------------------|-------------:|-------:|----------:| 141 | | LightResults: `Result.Failure().ToString()` | 5.331 ns | 1.00 | - | 142 | | FluentResults: `Result.Fail("").ToString()` | 1,177.864 ns | 220.96 | 5520 B | 143 | | ArdalisResult: `Result.Error().ToString()` | 15.082 ns | 2.83 | - | 144 | 145 | #### String representation of a failed value result with an error message 146 | | Method | Mean | Ratio | Allocated | Alloc Ratio | 147 | |------------------------------------------------------------|-------------:|------:|----------:|------------:| 148 | | LightResults: `Result.Failure(errorMessage).ToString()` | 105.022 ns | 1.00 | 1600 B | 1.00 | 149 | | FluentResults: `Result.Error(errorMessage).ToString()` | 1,720.919 ns | 16.39 | 11920 B | 7.45 | 150 | | ArdalisResult: `Result.Fail(errorMessage).ToString()` | 14.643 ns | 0.14 | - | 0.00 | 151 | -------------------------------------------------------------------------------- /docfx/docs/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Getting Started 2 | href: getting-started.md 3 | - name: Performance 4 | href: performance.md 5 | - name: Breaking Changes 6 | href: breaking-changes.md 7 | -------------------------------------------------------------------------------- /docfx/googlec98a83d0d9926e87.html: -------------------------------------------------------------------------------- 1 | google-site-verification: googlec98a83d0d9926e87.html -------------------------------------------------------------------------------- /docfx/images/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jscarle/LightResults/38e7a43eeead69b50be88a690153645c9ec8019e/docfx/images/Icon.png -------------------------------------------------------------------------------- /docfx/images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jscarle/LightResults/38e7a43eeead69b50be88a690153645c9ec8019e/docfx/images/Logo.png -------------------------------------------------------------------------------- /docfx/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | _layout: landing 3 | --- 4 | 5 | # LightResults - Operation Result Patterns for .NET 6 | 7 | Star 8 | Issue 9 | Sponsor 10 | 11 | LightResults is an extremely light and modern .NET library that provides a simple and flexible 12 | implementation of the Result Pattern. The Result Pattern is a way of representing the outcome 13 | of an operation, whether it's successful or has encountered an error, in a more explicit and 14 | structured manner. This project is heavily inspired by [Michael Altmann](https://github.com/altmann)'s 15 | excellent work with [FluentResults](https://github.com/altmann/FluentResults). 16 | 17 | [![test](https://img.shields.io/github/actions/workflow/status/jscarle/LightResults/test.yml?logo=github)](https://github.com/jscarle/LightResults) 18 | [![nuget](https://img.shields.io/nuget/v/LightResults)](https://www.nuget.org/packages/LightResults) 19 | [![downloads](https://img.shields.io/nuget/dt/LightResults)](https://www.nuget.org/packages/LightResults) 20 | 21 | ## References 22 | 23 | This library targets .NET Standard 2.0, .NET 6.0, .NET 7.0, .NET 8.0, and .NET 9.0. 24 | 25 | ## Dependencies 26 | 27 | This library has no dependencies. 28 | 29 | ## Advantages of this library 30 | 31 | - 🪶 Lightweight — Only contains what's necessary to implement the Result Pattern. 32 | - ⚙️ Extensible — Simple interfaces and base classes make it easy to adapt. 33 | - 🧱 Immutable — Results and errors are immutable and cannot be changed after being created. 34 | - 🧵 Thread-safe — Error and metadata collections are read-only. 35 | - ✨ Modern — Built against the latest version of .NET using the most recent best practices. 36 | - 🧪 Native — Written, compiled, and tested against the latest versions of .NET. 37 | - ❤️ Compatible — Available for dozens of versions of .NET as a [.NET Standard 2.0](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0) library. 38 | - 🪚 Trimmable — Compatible with [ahead-of-time compilation](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) (AOT) as of .NET 7.0. 39 | - 🚀 Performant — Heavily optimized and [benchmarked](https://jscarle.github.io/LightResults/docs/performance.html) to aim for the highest possible performance. 40 | 41 | ## Extensions 42 | 43 | Several [extensions are available](https://github.com/jscarle/LightResults.Extensions) to simplify implementation that use LightResults. 44 | -------------------------------------------------------------------------------- /docfx/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Docs 2 | href: docs/ 3 | - name: .NET Standard 2.0 4 | href: netstandard2.0/ 5 | - name: .NET 6.0 6 | href: net6.0/ 7 | - name: .NET 7.0 8 | href: net7.0/ 9 | - name: .NET 8.0 10 | href: net8.0/ 11 | - name: .NET 9.0 12 | href: net9.0/ 13 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | LightResults v9.0 Readme 2 | ======================== 3 | 4 | IMPORTANT: If you are upgrading from v8.0 of LightResults, there are significant breaking changes to be aware of. Please take a look at the README in the 5 | GitHub repository located at https://github.com/jscarle/LightResults or the Breaking Changes section of the documentation located at 6 | https://jscarle.github.io/LightResults/ for details regarding those changes and steps on how to migrate. 7 | -------------------------------------------------------------------------------- /src/LightResults/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | # CA1000: Do not declare static members on generic types 3 | # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1000 4 | dotnet_diagnostic.CA1000.severity = none 5 | 6 | # CA1716: Identifiers should not match keywords 7 | # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/CA1716 8 | dotnet_diagnostic.CA1716.severity = none 9 | -------------------------------------------------------------------------------- /src/LightResults/Common/IActionableResult.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0_OR_GREATER 2 | namespace LightResults.Common; 3 | 4 | /// Defines an actionable result. 5 | public interface IActionableResult : IResult 6 | where TResult : IResult 7 | { 8 | /// Creates a success result. 9 | /// A new instance of representing a success result with the specified value. 10 | static abstract TResult Success(); 11 | 12 | /// Creates a failure result. 13 | /// A new instance of representing a failure result. 14 | static abstract TResult Failure(); 15 | 16 | /// Creates a failure result with the given error message. 17 | /// The error message associated with the failure. 18 | /// A new instance of representing a failure result with the specified error message. 19 | static abstract TResult Failure(string errorMessage); 20 | 21 | /// Creates a failure result with the given error message and metadata. 22 | /// The error message associated with the failure. 23 | /// The metadata associated with the failure. 24 | /// A new instance of representing a failure result with the specified error message and metadata. 25 | static abstract TResult Failure(string errorMessage, (string Key, object? Value) metadata); 26 | 27 | /// Creates a failure result with the given error message and metadata. 28 | /// The error message associated with the failure. 29 | /// The metadata associated with the failure. 30 | /// A new instance of representing a failure result with the specified error message and metadata. 31 | static abstract TResult Failure(string errorMessage, KeyValuePair metadata); 32 | 33 | /// Creates a failure result with the given error message and metadata. 34 | /// The error message associated with the failure. 35 | /// The metadata associated with the failure. 36 | /// A new instance of representing a failure result with the specified error message and metadata. 37 | static abstract TResult Failure(string errorMessage, IReadOnlyDictionary metadata); 38 | 39 | /// Creates a failure result with the given exception. 40 | /// The associated with the failure, if any. 41 | /// A new instance of representing a failure result with the specified exception. 42 | /// The exception is added to the error under the key of "Exception" and the error is set to that of the exception. 43 | static abstract TResult Failure(Exception? ex); 44 | 45 | /// Creates a failure result with the given error message and exception. 46 | /// The error message associated with the failure. 47 | /// The associated with the failure, if any. 48 | /// A new instance of representing a failure result with the specified error message and exception. 49 | /// The exception is added to the error under the key of "Exception". 50 | static abstract TResult Failure(string errorMessage, Exception? ex); 51 | 52 | /// Creates a failure result with the given error. 53 | /// The error associated with the failure. 54 | /// A new instance of representing a failure result with the specified error. 55 | static abstract TResult Failure(IError error); 56 | 57 | /// Creates a failure result with the given errors. 58 | /// A collection of errors associated with the failure. 59 | /// A new instance of representing a failure result with the specified errors. 60 | static abstract TResult Failure(IEnumerable errors); 61 | 62 | /// Creates a failure result with the given errors. 63 | /// A collection of errors associated with the failure. 64 | /// A new instance of representing a failure result with the specified errors. 65 | static abstract TResult Failure(IReadOnlyList errors); 66 | } 67 | 68 | /// Defines an actionable result. 69 | public interface IActionableResult : IResult 70 | where TResult : IResult 71 | { 72 | /// Creates a success result with the given value. 73 | /// The value to include in the result. 74 | /// A new instance of representing a success result with the specified value. 75 | static abstract TResult Success(TValue value); 76 | 77 | /// Creates a failure result. 78 | /// A new instance of representing a failure result. 79 | static abstract TResult Failure(); 80 | 81 | /// Creates a failure result with the given error message. 82 | /// The error message associated with the failure. 83 | /// A new instance of representing a failure result with the specified error message. 84 | static abstract TResult Failure(string errorMessage); 85 | 86 | /// Creates a failure result with the given error message and metadata. 87 | /// The error message associated with the failure. 88 | /// The metadata associated with the failure. 89 | /// A new instance of representing a failure result with the specified error message and metadata. 90 | static abstract TResult Failure(string errorMessage, (string Key, object? Value) metadata); 91 | 92 | /// Creates a failure result with the given error message and metadata. 93 | /// The error message associated with the failure. 94 | /// The metadata associated with the failure. 95 | /// A new instance of representing a failure result with the specified error message and metadata. 96 | static abstract TResult Failure(string errorMessage, KeyValuePair metadata); 97 | 98 | /// Creates a failure result with the given error message and metadata. 99 | /// The error message associated with the failure. 100 | /// The metadata associated with the failure. 101 | /// A new instance of representing a failure result with the specified error message and metadata. 102 | static abstract TResult Failure(string errorMessage, IReadOnlyDictionary metadata); 103 | 104 | /// Creates a failure result with the given exception. 105 | /// The associated with the failure, if any. 106 | /// A new instance of representing a failure result with the specified exception. 107 | /// The exception is added to the error under the key of "Exception" and the error is set to that of the exception. 108 | static abstract TResult Failure(Exception? ex); 109 | 110 | /// Creates a failure result with the given error message and exception. 111 | /// The error message associated with the failure. 112 | /// The associated with the failure, if any. 113 | /// A new instance of representing a failure result with the specified error message and exception. 114 | /// The exception is added to the error under the key of "Exception". 115 | static abstract TResult Failure(string errorMessage, Exception? ex); 116 | 117 | /// Creates a failure result with the given error. 118 | /// The error associated with the failure. 119 | /// A new instance of representing a failure result with the specified error. 120 | static abstract TResult Failure(IError error); 121 | 122 | /// Creates a failure result with the given errors. 123 | /// A collection of errors associated with the failure. 124 | /// A new instance of representing a failure result with the specified errors. 125 | static abstract TResult Failure(IEnumerable errors); 126 | 127 | /// Creates a failure result with the given errors. 128 | /// A collection of errors associated with the failure. 129 | /// A new instance of representing a failure result with the specified errors. 130 | static abstract TResult Failure(IReadOnlyList errors); 131 | } 132 | #endif 133 | -------------------------------------------------------------------------------- /src/LightResults/Common/SingleItemMetadataDictionary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace LightResults.Common; 5 | 6 | /// A minimal read-only dictionary optimized for a single metadata entry. 7 | internal sealed class SingleItemMetadataDictionary : IReadOnlyDictionary 8 | { 9 | private readonly string _key; 10 | private readonly object? _value; 11 | 12 | public SingleItemMetadataDictionary(string key, object? value) 13 | { 14 | _key = key; 15 | _value = value; 16 | } 17 | 18 | public int Count => 1; 19 | 20 | public object? this[string key] => string.Equals(key, _key, StringComparison.Ordinal) ? _value : throw new KeyNotFoundException(); 21 | 22 | IEnumerable IReadOnlyDictionary.Keys => new KeyEnumerable(_key); 23 | 24 | IEnumerable IReadOnlyDictionary.Values => new ValueEnumerable(_value); 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public bool ContainsKey(string key) 28 | { 29 | return string.Equals(key, _key, StringComparison.Ordinal); 30 | } 31 | 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | public bool TryGetValue(string key, out object? value) 34 | { 35 | if (ContainsKey(key)) 36 | { 37 | value = _value; 38 | return true; 39 | } 40 | 41 | value = null; 42 | return false; 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | private Enumerator GetEnumerator() 47 | { 48 | return new Enumerator(_key, _value); 49 | } 50 | 51 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 52 | IEnumerator IEnumerable.GetEnumerator() 53 | { 54 | return GetEnumerator(); 55 | } 56 | 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | IEnumerator> IEnumerable>.GetEnumerator() 59 | { 60 | return GetEnumerator(); 61 | } 62 | 63 | private struct Enumerator(string key, object? value) : IEnumerator> 64 | { 65 | private bool _moved; 66 | 67 | public KeyValuePair Current { get; } = new(key, value); 68 | 69 | object IEnumerator.Current => Current; 70 | 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public bool MoveNext() 73 | { 74 | if (_moved) 75 | return false; 76 | 77 | _moved = true; 78 | return true; 79 | } 80 | 81 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 82 | public void Reset() 83 | { 84 | _moved = false; 85 | } 86 | 87 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 88 | public void Dispose() 89 | { 90 | } 91 | } 92 | 93 | private struct KeyEnumerable(string key) : IEnumerable, IEnumerator 94 | { 95 | private bool _moved; 96 | 97 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 98 | IEnumerator IEnumerable.GetEnumerator() 99 | { 100 | return this; 101 | } 102 | 103 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 104 | IEnumerator IEnumerable.GetEnumerator() 105 | { 106 | return this; 107 | } 108 | 109 | public string Current => key; 110 | 111 | object IEnumerator.Current => key; 112 | 113 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 114 | public bool MoveNext() 115 | { 116 | if (_moved) 117 | return false; 118 | 119 | _moved = true; 120 | return true; 121 | } 122 | 123 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 124 | public void Reset() 125 | { 126 | _moved = false; 127 | } 128 | 129 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 130 | public void Dispose() 131 | { 132 | } 133 | } 134 | 135 | private struct ValueEnumerable(object? value) : IEnumerable, IEnumerator 136 | { 137 | private bool _moved; 138 | 139 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 140 | IEnumerator IEnumerable.GetEnumerator() 141 | { 142 | return this; 143 | } 144 | 145 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 146 | IEnumerator IEnumerable.GetEnumerator() 147 | { 148 | return this; 149 | } 150 | 151 | public object? Current => value; 152 | 153 | object IEnumerator.Current => value!; 154 | 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | public bool MoveNext() 157 | { 158 | if (_moved) 159 | return false; 160 | 161 | _moved = true; 162 | return true; 163 | } 164 | 165 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 166 | public void Reset() 167 | { 168 | _moved = false; 169 | } 170 | 171 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 172 | public void Dispose() 173 | { 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/LightResults/Common/StringHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace LightResults.Common; 5 | 6 | internal static class StringHelper 7 | { 8 | private const string PreResultStr = "Result { IsSuccess = "; 9 | private const string SuccessResultStr = "True"; 10 | private const string FailureResultStr = "False"; 11 | private const string PreValueStr = ", Value = "; 12 | private const string CharStr = "'"; 13 | private const string StringStr = "\""; 14 | private const string PreErrorStr = ", Error = \""; 15 | private const string PostErrorStr = "\""; 16 | private const string PostResultStr = " }"; 17 | private const string PreMessageStr = " { Message = \""; 18 | private const string PostMessageStr = "\" }"; 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | public static string GetResultValueString(T value) 22 | { 23 | switch (value) 24 | { 25 | case bool booleanValue: 26 | return GetResultValueValueString(booleanValue.ToString()); 27 | case sbyte sbyteValue: 28 | return GetResultValueValueString(sbyteValue.ToString(CultureInfo.InvariantCulture)); 29 | case byte byteValue: 30 | return GetResultValueValueString(byteValue.ToString(CultureInfo.InvariantCulture)); 31 | case short shortValue: 32 | return GetResultValueValueString(shortValue.ToString(CultureInfo.InvariantCulture)); 33 | case ushort uShortValue: 34 | return GetResultValueValueString(uShortValue.ToString(CultureInfo.InvariantCulture)); 35 | case int intValue: 36 | return GetResultValueValueString(intValue.ToString(CultureInfo.InvariantCulture)); 37 | case uint uintValue: 38 | return GetResultValueValueString(uintValue.ToString(CultureInfo.InvariantCulture)); 39 | case long longValue: 40 | return GetResultValueValueString(longValue.ToString(CultureInfo.InvariantCulture)); 41 | case ulong ulongValue: 42 | return GetResultValueValueString(ulongValue.ToString(CultureInfo.InvariantCulture)); 43 | #if NET7_0_OR_GREATER 44 | case Int128 int128Value: 45 | return GetResultValueValueString(int128Value.ToString(CultureInfo.InvariantCulture)); 46 | case UInt128 uint128Value: 47 | return GetResultValueValueString(uint128Value.ToString(CultureInfo.InvariantCulture)); 48 | #endif 49 | case decimal decimalValue: 50 | return GetResultValueValueString(decimalValue.ToString(CultureInfo.InvariantCulture)); 51 | case float floatValue: 52 | return GetResultValueValueString(floatValue.ToString(CultureInfo.InvariantCulture)); 53 | case double doubleValue: 54 | return GetResultValueValueString(doubleValue.ToString(CultureInfo.InvariantCulture)); 55 | case DateTime dateTimeValue: 56 | return GetResultStringValueString(dateTimeValue.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK", CultureInfo.InvariantCulture)); 57 | case DateTimeOffset dateTimeOffsetValue: 58 | return GetResultStringValueString(dateTimeOffsetValue.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK", CultureInfo.InvariantCulture)); 59 | #if NET6_0_OR_GREATER 60 | case DateOnly dateOnlyValue: 61 | return GetResultStringValueString(dateOnlyValue.ToString("yyyy'-'MM'-'dd", CultureInfo.InvariantCulture)); 62 | case TimeOnly timeOnlyValue: 63 | return GetResultStringValueString(timeOnlyValue.ToString("HH':'mm':'ss", CultureInfo.InvariantCulture)); 64 | #endif 65 | case char charValue: 66 | return GetResultCharValueString(charValue.ToString()); 67 | case string stringValue: 68 | return GetResultStringValueString(stringValue); 69 | case IFormattable formattableValue: 70 | return GetResultValueString(formattableValue); 71 | default: 72 | return "Result { IsSuccess = True }"; 73 | } 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public static string GetResultValueString(IFormattable value) 78 | { 79 | return GetResultValueValueString(value.ToString(null, CultureInfo.InvariantCulture)); 80 | } 81 | 82 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 83 | private static string GetResultCharValueString(string valueString) 84 | { 85 | #if NET6_0_OR_GREATER 86 | var stringLength = PreResultStrLength 87 | + SuccessResultStrLength 88 | + PreValueStrLength 89 | + CharStrLength 90 | + valueString.Length 91 | + CharStrLength 92 | + PostResultStrLength; 93 | return string.Create(stringLength, valueString, GetResultValueCharSpan); 94 | #else 95 | return $"{PreResultStr}{SuccessResultStr}{PreValueStr}{CharStr}{valueString}{CharStr}{PostResultStr}"; 96 | #endif 97 | } 98 | 99 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 100 | private static string GetResultStringValueString(string valueString) 101 | { 102 | #if NET6_0_OR_GREATER 103 | var stringLength = PreResultStrLength 104 | + SuccessResultStrLength 105 | + PreValueStrLength 106 | + StringStrLength 107 | + valueString.Length 108 | + StringStrLength 109 | + PostResultStrLength; 110 | return string.Create(stringLength, valueString, GetResultValueStringSpan); 111 | #else 112 | return $"{PreResultStr}{SuccessResultStr}{PreValueStr}{StringStr}{valueString}{StringStr}{PostResultStr}"; 113 | #endif 114 | } 115 | 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | private static string GetResultValueValueString(string valueString) 118 | { 119 | #if NET6_0_OR_GREATER 120 | var stringLength = PreResultStrLength + SuccessResultStrLength + PreValueStrLength + valueString.Length + PostResultStrLength; 121 | return string.Create(stringLength, valueString, GetResultValueSpan); 122 | #else 123 | return $"{PreResultStr}{SuccessResultStr}{PreValueStr}{valueString}{PostResultStr}"; 124 | #endif 125 | } 126 | 127 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 128 | public static string GetResultErrorString(string errorMessage) 129 | { 130 | #if NET6_0_OR_GREATER 131 | var stringLength = PreResultStrLength + FailureResultStrLength + PreErrorStrLength + errorMessage.Length + PostErrorStrLength + PostResultStrLength; 132 | return string.Create(stringLength, errorMessage, GetResultErrorSpan); 133 | #else 134 | return $"{PreResultStr}{FailureResultStr}{PreErrorStr}{errorMessage}{PostErrorStr}{PostResultStr}"; 135 | #endif 136 | } 137 | 138 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 139 | public static string GetErrorString(string type, string message) 140 | { 141 | #if NET6_0_OR_GREATER 142 | var stringLength = type.Length + PreMessageStrLength + message.Length + PostMessageStrLength; 143 | return string.Create(stringLength, (errorType: type, errorMessage: message), GetErrorSpan); 144 | #else 145 | return $"{type}{PreMessageStr}{message}{PostMessageStr}"; 146 | #endif 147 | } 148 | 149 | #if NET6_0_OR_GREATER 150 | private const int PreResultStrLength = 21; 151 | private const int SuccessResultStrLength = 4; 152 | private const int FailureResultStrLength = 5; 153 | private const int PreValueStrLength = 10; 154 | private const int CharStrLength = 1; 155 | private const int StringStrLength = 1; 156 | private const int PreErrorStrLength = 11; 157 | private const int PostErrorStrLength = 1; 158 | private const int PostResultStrLength = 2; 159 | private const int PreMessageStrLength = 14; 160 | private const int PostMessageStrLength = 3; 161 | 162 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 163 | private static void GetResultValueSpan(Span span, string state) 164 | { 165 | PreResultStr.AsSpan() 166 | .CopyTo(span); 167 | span = span[PreResultStrLength..]; 168 | SuccessResultStr.AsSpan() 169 | .CopyTo(span); 170 | span = span[SuccessResultStrLength..]; 171 | PreValueStr.AsSpan() 172 | .CopyTo(span); 173 | span = span[PreValueStrLength..]; 174 | state 175 | .CopyTo(span); 176 | span = span[state.Length..]; 177 | PostResultStr.AsSpan() 178 | .CopyTo(span); 179 | } 180 | 181 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 182 | private static void GetResultValueCharSpan(Span span, string state) 183 | { 184 | PreResultStr.AsSpan() 185 | .CopyTo(span); 186 | span = span[PreResultStrLength..]; 187 | SuccessResultStr.AsSpan() 188 | .CopyTo(span); 189 | span = span[SuccessResultStrLength..]; 190 | PreValueStr.AsSpan() 191 | .CopyTo(span); 192 | span = span[PreValueStrLength..]; 193 | CharStr.AsSpan() 194 | .CopyTo(span); 195 | span = span[CharStrLength..]; 196 | state.AsSpan() 197 | .CopyTo(span); 198 | span = span[state.Length..]; 199 | CharStr.AsSpan() 200 | .CopyTo(span); 201 | span = span[CharStrLength..]; 202 | PostResultStr.AsSpan() 203 | .CopyTo(span); 204 | } 205 | 206 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 207 | private static void GetResultValueStringSpan(Span span, string state) 208 | { 209 | PreResultStr.AsSpan() 210 | .CopyTo(span); 211 | span = span[PreResultStrLength..]; 212 | SuccessResultStr.AsSpan() 213 | .CopyTo(span); 214 | span = span[SuccessResultStrLength..]; 215 | PreValueStr.AsSpan() 216 | .CopyTo(span); 217 | span = span[PreValueStrLength..]; 218 | StringStr.AsSpan() 219 | .CopyTo(span); 220 | span = span[StringStrLength..]; 221 | state.AsSpan() 222 | .CopyTo(span); 223 | span = span[state.Length..]; 224 | StringStr.AsSpan() 225 | .CopyTo(span); 226 | span = span[StringStrLength..]; 227 | PostResultStr.AsSpan() 228 | .CopyTo(span); 229 | } 230 | 231 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 232 | private static void GetResultErrorSpan(Span span, string state) 233 | { 234 | PreResultStr.AsSpan() 235 | .CopyTo(span); 236 | span = span[PreResultStrLength..]; 237 | FailureResultStr.AsSpan() 238 | .CopyTo(span); 239 | span = span[FailureResultStrLength..]; 240 | PreErrorStr.AsSpan() 241 | .CopyTo(span); 242 | span = span[PreErrorStrLength..]; 243 | state.AsSpan() 244 | .CopyTo(span); 245 | span = span[state.Length..]; 246 | PostErrorStr.AsSpan() 247 | .CopyTo(span); 248 | span = span[PostErrorStrLength..]; 249 | PostResultStr.AsSpan() 250 | .CopyTo(span); 251 | } 252 | 253 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 254 | private static void GetErrorSpan(Span span, (string errorType, string errorMessage) state) 255 | { 256 | state.errorType 257 | .AsSpan() 258 | .CopyTo(span); 259 | span = span[state.errorType.Length..]; 260 | PreMessageStr.AsSpan() 261 | .CopyTo(span); 262 | span = span[PreMessageStrLength..]; 263 | state.errorMessage 264 | .AsSpan() 265 | .CopyTo(span); 266 | span = span[state.errorMessage.Length..]; 267 | PostMessageStr.AsSpan() 268 | .CopyTo(span); 269 | } 270 | #endif 271 | } 272 | -------------------------------------------------------------------------------- /src/LightResults/Error.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Runtime.CompilerServices; 3 | using LightResults.Common; 4 | 5 | namespace LightResults; 6 | 7 | /// Represents an error with a message and associated metadata. 8 | [SuppressMessage("Major Code Smell", "S4035:Classes implementing \"IEquatable\" should be sealed", 9 | Justification = "This class is not sealed to allow for inheritance." 10 | )] 11 | public class Error : IError, IEquatable 12 | { 13 | /// Gets an empty instance. 14 | public static IError Empty { get; } = new Error("", new Dictionary()); 15 | 16 | /// 17 | public string Message { get; init; } 18 | 19 | /// 20 | public Exception? Exception 21 | { 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | get 24 | { 25 | if (Metadata.TryGetValue(ExceptionKey, out var value) && value is Exception ex) 26 | return ex; 27 | 28 | return null; 29 | } 30 | } 31 | 32 | /// 33 | public IReadOnlyDictionary Metadata { get; init; } 34 | 35 | internal static IReadOnlyList EmptyErrorList { get; } = []; 36 | internal static IReadOnlyList DefaultErrorList { get; } = [Empty]; 37 | 38 | private const string ErrorTypeName = nameof(Error); 39 | private const string ExceptionKey = "Exception"; 40 | private static readonly IReadOnlyDictionary EmptyMetaData = new Dictionary(); 41 | 42 | /// Initializes a new instance of the class. 43 | public Error() 44 | { 45 | Message = ""; 46 | Metadata = EmptyMetaData; 47 | } 48 | 49 | /// Initializes a new instance of the class with the specified error message. 50 | /// The error message. 51 | public Error(string message) 52 | { 53 | Message = message; 54 | Metadata = EmptyMetaData; 55 | } 56 | 57 | /// Initializes a new instance of the class with the specified exception. 58 | /// The associated with the error. 59 | /// The is added to under the key of "Exception" and the is set to the exception message if present. 60 | public Error(Exception? exception) 61 | { 62 | if (exception is null) 63 | { 64 | Message = ""; 65 | Metadata = EmptyMetaData; 66 | return; 67 | } 68 | 69 | var errorMessage = exception switch 70 | { 71 | _ when string.IsNullOrEmpty(exception.Message) => $"An exception of type {exception.GetType().Name} was thrown.", 72 | _ => $"{exception.GetType().Name}: {exception.Message}", 73 | }; 74 | 75 | Message = errorMessage; 76 | Metadata = new SingleItemMetadataDictionary(ExceptionKey, exception); 77 | } 78 | 79 | /// Initializes a new instance of the class with the specified error message and exception. 80 | /// The error message. 81 | /// The associated with the error. 82 | /// The is added to under the key of "Exception". 83 | public Error(string message, Exception? exception) 84 | { 85 | if (exception is null) 86 | { 87 | Message = message; 88 | Metadata = EmptyMetaData; 89 | return; 90 | } 91 | 92 | Message = message; 93 | Metadata = new SingleItemMetadataDictionary(ExceptionKey, exception); 94 | } 95 | 96 | /// Initializes a new instance of the class with the specified error message and metadata. 97 | /// The error message. 98 | /// The metadata associated with the error. 99 | public Error(string message, (string Key, object? Value) metadata) 100 | { 101 | Message = message; 102 | Metadata = new SingleItemMetadataDictionary(metadata.Key, metadata.Value); 103 | } 104 | 105 | /// Initializes a new instance of the class with the specified error message and metadata. 106 | /// The error message. 107 | /// The metadata associated with the error. 108 | public Error(string message, KeyValuePair metadata) 109 | { 110 | Message = message; 111 | Metadata = new SingleItemMetadataDictionary(metadata.Key, metadata.Value); 112 | } 113 | 114 | #if NET6_0_OR_GREATER 115 | /// Initializes a new instance of the class with the specified error message and metadata. 116 | /// The error message. 117 | /// The metadata associated with the error. 118 | public Error(string message, IEnumerable> metadata) 119 | { 120 | Message = message; 121 | Metadata = new Dictionary(metadata) 122 | #if NET7_0_OR_GREATER 123 | .AsReadOnly() 124 | #endif 125 | ; 126 | } 127 | #endif 128 | 129 | /// Initializes a new instance of the class with the specified error message and metadata. 130 | /// The error message. 131 | /// The metadata associated with the error. 132 | public Error(string message, IReadOnlyDictionary metadata) 133 | { 134 | Message = message; 135 | Metadata = metadata; 136 | } 137 | 138 | /// Determines whether the specified is equal to this instance. 139 | /// The to compare with this instance. 140 | /// true if the specified is equal to this instance; otherwise, false. 141 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 142 | public bool Equals(Error? other) 143 | { 144 | if (ReferenceEquals(null, other)) 145 | return false; 146 | 147 | if (ReferenceEquals(this, other)) 148 | return true; 149 | 150 | if (!Message.Equals(other.Message, StringComparison.Ordinal)) 151 | return false; 152 | 153 | if (Metadata.Count != other.Metadata.Count) 154 | return false; 155 | 156 | foreach (var kvp in Metadata) 157 | { 158 | if (!other.Metadata.TryGetValue(kvp.Key, out var otherValue)) 159 | return false; 160 | 161 | if (!Equals(kvp.Value, otherValue)) 162 | return false; 163 | } 164 | 165 | return true; 166 | } 167 | 168 | /// 169 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 170 | public override bool Equals(object? obj) 171 | { 172 | return obj is Error other && Equals(other); 173 | } 174 | 175 | /// 176 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 177 | public override int GetHashCode() 178 | { 179 | var hash = new HashCode(); 180 | hash.Add(Message, StringComparer.Ordinal); 181 | 182 | foreach (var kvp in Metadata) 183 | { 184 | hash.Add(kvp.Key, StringComparer.Ordinal); 185 | hash.Add(kvp.Value); 186 | } 187 | 188 | return hash.ToHashCode(); 189 | } 190 | 191 | /// Determines whether two instances are equal. 192 | /// The first instance to compare. 193 | /// The second instance to compare. 194 | /// true if the specified instances are equal; otherwise, false. 195 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 196 | public static bool operator ==(Error? left, Error? right) 197 | { 198 | if (left is null) 199 | return right is null; 200 | 201 | return left.Equals(right); 202 | } 203 | 204 | /// Determines whether two instances are not equal. 205 | /// The first instance to compare. 206 | /// The second instance to compare. 207 | /// true if the specified instances are not equal; otherwise, false. 208 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 209 | public static bool operator !=(Error? left, Error? right) 210 | { 211 | return !(left == right); 212 | } 213 | 214 | /// 215 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 216 | public override string ToString() 217 | { 218 | var type = GetType(); 219 | var errorType = type == typeof(Error) ? ErrorTypeName : type.Name; 220 | 221 | if (Message.Length == 0) 222 | return errorType; 223 | 224 | return StringHelper.GetErrorString(errorType, Message); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/LightResults/IError.cs: -------------------------------------------------------------------------------- 1 | namespace LightResults; 2 | 3 | /// Defines an error with a message and associated metadata. 4 | public interface IError 5 | { 6 | /// Gets the error message. 7 | /// A representing the error message. 8 | string Message { get; } 9 | 10 | /// Gets the associated with the error if one exists. 11 | /// An instance when the metadata contains an entry named "Exception" with a value of type ; otherwise, . 12 | Exception? Exception { get; } 13 | 14 | /// Gets the metadata associated with the error. 15 | /// An containing the metadata. 16 | /// 17 | /// The metadata is represented as a read-only dictionary of key-value pairs, where the keys are and the values are 18 | /// which may be . 19 | /// 20 | IReadOnlyDictionary Metadata { get; } 21 | } 22 | -------------------------------------------------------------------------------- /src/LightResults/IResult.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace LightResults; 4 | 5 | /// Defines a result. 6 | public interface IResult 7 | { 8 | /// Gets a read-only collection of errors associated with the result. 9 | /// An of instances representing the errors. 10 | IReadOnlyCollection Errors { get; } 11 | 12 | /// Determines whether the result was successful. 13 | /// true if the result was successful; otherwise, false. 14 | bool IsSuccess(); 15 | 16 | /// Determines whether the result is a failure. 17 | /// true if the result is a failure; otherwise, false. 18 | bool IsFailure(); 19 | 20 | /// Determines whether the result is a failure. 21 | /// The error of the result. 22 | /// true if the result is a failure; otherwise, false. 23 | bool IsFailure([MaybeNullWhen(false)] out IError error); 24 | 25 | /// Checks if the result contains an error of the specified type. 26 | /// The type of error to check for. 27 | /// true if an error of the specified type exists; otherwise, false. 28 | bool HasError() 29 | where TError : IError; 30 | 31 | /// Checks if the result contains an error of the specified type. 32 | /// The error of the specified type. 33 | /// The type of error to check for. 34 | /// true if an error of the specified type exists; otherwise, false. 35 | bool HasError([MaybeNullWhen(false)] out TError error) 36 | where TError : IError; 37 | } 38 | -------------------------------------------------------------------------------- /src/LightResults/IResult`1.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace LightResults; 4 | 5 | /// Defines a result with a value. 6 | /// The type of value. 7 | public interface IResult : IResult 8 | { 9 | /// Determines whether the result was successful. 10 | /// The value of the result. 11 | /// true if the result was successful; otherwise, false. 12 | bool IsSuccess([MaybeNullWhen(false)] out TValue value); 13 | 14 | /// Determines whether the result was successful. 15 | /// The value of the result. 16 | /// The error of the result. 17 | /// true if the result was successful; otherwise, false. 18 | bool IsSuccess([MaybeNullWhen(false)] out TValue value, [MaybeNullWhen(true)] out IError error); 19 | 20 | /// Determines whether the result is a failure. 21 | /// The error of the result. 22 | /// The value of the result. 23 | /// true if the result is a failure; otherwise, false. 24 | bool IsFailure([MaybeNullWhen(false)] out IError error, [MaybeNullWhen(true)] out TValue value); 25 | } 26 | -------------------------------------------------------------------------------- /src/LightResults/LightResults.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LightResults 6 | netstandard2.0;net6.0;net7.0;net8.0;net9.0 7 | enable 8 | enable 9 | latest 10 | 11 | 12 | 13 | 14 | latest-All 15 | true 16 | 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | all 31 | runtime; build; native; contentfiles; analyzers; buildtransitive 32 | 33 | 34 | 35 | 36 | 37 | 38 | LightResults 39 | 9.0.5 40 | 9.0.5.0 41 | 9.0.5.0 42 | en-US 43 | true 44 | true 45 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 46 | 47 | 48 | 49 | 50 | true 51 | LightResults 52 | LightResults 53 | An extremely light and modern Result Pattern library. 54 | README.md 55 | https://github.com/jscarle/LightResults 56 | result results pattern fluentresults error handling 57 | https://github.com/jscarle/LightResults 58 | git 59 | Jean-Sebastien Carle 60 | Copyright © Jean-Sebastien Carle 2024 61 | LICENSE.md 62 | Icon.png 63 | true 64 | snupkg 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/LightResults/Result`1.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Runtime.CompilerServices; 3 | using LightResults.Common; 4 | 5 | namespace LightResults; 6 | 7 | // ReSharper disable StaticMemberInGenericType 8 | /// Represents a result. 9 | /// The type of the value of the result. 10 | public readonly struct Result : IEquatable>, 11 | #if NET7_0_OR_GREATER 12 | IActionableResult> 13 | #else 14 | IResult 15 | #endif 16 | { 17 | /// 18 | public IReadOnlyCollection Errors 19 | { 20 | get 21 | { 22 | if (_errors is not null) 23 | return _errors; 24 | 25 | if (_isSuccess) 26 | return Error.EmptyErrorList; 27 | 28 | return Error.DefaultErrorList; 29 | } 30 | } 31 | 32 | internal static readonly Result FailureResult = new(Error.Empty); 33 | private readonly bool _isSuccess = false; 34 | private readonly IReadOnlyList? _errors; 35 | private readonly TValue? _valueOrDefault; 36 | 37 | internal Result(TValue value) 38 | { 39 | _isSuccess = true; 40 | _valueOrDefault = value; 41 | } 42 | 43 | internal Result(IError error) 44 | { 45 | _errors = [error]; 46 | } 47 | 48 | internal Result(IEnumerable errors) 49 | { 50 | if (errors is IReadOnlyList list) 51 | { 52 | _errors = list; 53 | return; 54 | } 55 | 56 | if (errors is ICollection collection) 57 | { 58 | var array = new IError[collection.Count]; 59 | collection.CopyTo(array, 0); 60 | _errors = array; 61 | return; 62 | } 63 | 64 | _errors = errors.ToArray(); 65 | } 66 | 67 | private Result(IReadOnlyList errors) 68 | { 69 | _errors = errors; 70 | } 71 | 72 | /// 73 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 74 | public bool IsSuccess() 75 | { 76 | return _isSuccess; 77 | } 78 | 79 | /// 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public bool IsSuccess([MaybeNullWhen(false)] out TValue value) 82 | { 83 | value = _valueOrDefault; 84 | return _isSuccess; 85 | } 86 | 87 | /// 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | public bool IsSuccess([MaybeNullWhen(false)] out TValue value, [MaybeNullWhen(true)] out IError error) 90 | { 91 | if (_isSuccess) 92 | { 93 | value = _valueOrDefault; 94 | error = null; 95 | } 96 | else 97 | { 98 | value = default; 99 | if (_errors is not null) 100 | error = _errors[0]; 101 | else 102 | error = Error.Empty; 103 | } 104 | 105 | return _isSuccess; 106 | } 107 | 108 | /// 109 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 110 | public bool IsFailure() 111 | { 112 | return !_isSuccess; 113 | } 114 | 115 | /// 116 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 | public bool IsFailure([MaybeNullWhen(false)] out IError error) 118 | { 119 | if (_isSuccess) 120 | error = null; 121 | else if (_errors is not null) 122 | error = _errors[0]; 123 | else 124 | error = Error.Empty; 125 | 126 | return !_isSuccess; 127 | } 128 | 129 | /// 130 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 131 | public bool IsFailure([MaybeNullWhen(false)] out IError error, [MaybeNullWhen(true)] out TValue value) 132 | { 133 | if (_isSuccess) 134 | { 135 | value = _valueOrDefault; 136 | error = null; 137 | } 138 | else 139 | { 140 | value = default; 141 | if (_errors is not null) 142 | error = _errors[0]; 143 | else 144 | error = Error.Empty; 145 | } 146 | 147 | return !_isSuccess; 148 | } 149 | 150 | #if NET7_0_OR_GREATER 151 | /// Creates a success result with the specified value. 152 | /// The value to include in the result. 153 | /// A new instance of representing a success result with the specified value. 154 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 155 | static Result IActionableResult>.Success(TValue value) 156 | { 157 | return new Result(value); 158 | } 159 | 160 | /// Creates a failure result. 161 | /// A new instance of representing a failure result. 162 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 163 | static Result IActionableResult>.Failure() 164 | { 165 | return FailureResult; 166 | } 167 | 168 | /// Creates a failure result with the given error message. 169 | /// The error message associated with the failure. 170 | /// A new instance of representing a failure result with the specified error message. 171 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 172 | static Result IActionableResult>.Failure(string errorMessage) 173 | { 174 | var error = new Error(errorMessage); 175 | return new Result(error); 176 | } 177 | 178 | /// Creates a failure result with the given error message and metadata. 179 | /// The error message associated with the failure. 180 | /// The metadata associated with the failure. 181 | /// A new instance of representing a failure result with the specified error message. 182 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 183 | static Result IActionableResult>.Failure(string errorMessage, (string Key, object? Value) metadata) 184 | { 185 | var dictionary = new SingleItemMetadataDictionary(metadata.Key, metadata.Value); 186 | var error = new Error(errorMessage, dictionary); 187 | return new Result(error); 188 | } 189 | 190 | /// Creates a failure result with the given error message and metadata. 191 | /// The error message associated with the failure. 192 | /// The metadata associated with the failure. 193 | /// A new instance of representing a failure result with the specified error message. 194 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 195 | static Result IActionableResult>.Failure(string errorMessage, KeyValuePair metadata) 196 | { 197 | var dictionary = new SingleItemMetadataDictionary(metadata.Key, metadata.Value); 198 | var error = new Error(errorMessage, dictionary); 199 | return new Result(error); 200 | } 201 | 202 | /// Creates a failure result with the given error message and metadata. 203 | /// The error message associated with the failure. 204 | /// The metadata associated with the failure. 205 | /// A new instance of representing a failure result with the specified error message. 206 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 207 | static Result IActionableResult>.Failure(string errorMessage, IReadOnlyDictionary metadata) 208 | { 209 | var error = new Error(errorMessage, metadata); 210 | return new Result(error); 211 | } 212 | 213 | /// Creates a failure result with the given exception. 214 | /// The associated with the failure, if any. 215 | /// A new instance of representing a failure result with the specified exception. 216 | /// The exception is added to the error under the key of "Exception" and the error is set to that of the exception. 217 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 218 | static Result IActionableResult>.Failure(Exception? ex) 219 | { 220 | var error = new Error(ex); 221 | return new Result(error); 222 | } 223 | 224 | /// Creates a failure result with the given error message and exception. 225 | /// The error message associated with the failure. 226 | /// The associated with the failure, if any. 227 | /// A new instance of representing a failure result with the specified error message and exception. 228 | /// The exception is added to the error under the key of "Exception". 229 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 230 | static Result IActionableResult>.Failure(string errorMessage, Exception? ex) 231 | { 232 | var error = new Error(errorMessage, ex); 233 | return new Result(error); 234 | } 235 | 236 | /// Creates a failure result with the given error. 237 | /// The error associated with the failure. 238 | /// A new instance of representing a failure result with the specified error. 239 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 240 | static Result IActionableResult>.Failure(IError error) 241 | { 242 | return new Result(error); 243 | } 244 | 245 | /// Creates a failure result with the given errors. 246 | /// A collection of errors associated with the failure. 247 | /// A new instance of representing a failure result with the specified errors. 248 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 249 | static Result IActionableResult>.Failure(IEnumerable errors) 250 | { 251 | return new Result(errors); 252 | } 253 | 254 | /// Creates a failure result with the given errors. 255 | /// A collection of errors associated with the failure. 256 | /// A new instance of representing a failure result with the specified errors. 257 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 258 | static Result IActionableResult>.Failure(IReadOnlyList errors) 259 | { 260 | return new Result(errors); 261 | } 262 | #endif 263 | 264 | /// 265 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 266 | public bool HasError() 267 | where TError : IError 268 | { 269 | if (_isSuccess) 270 | return false; 271 | 272 | if (_errors is not null) 273 | // Do not convert to LINQ, this creates unnecessary heap allocations. 274 | // For is the most efficient way to loop. It is the fastest and does not allocate. 275 | // ReSharper disable once ForCanBeConvertedToForeach 276 | // ReSharper disable once LoopCanBeConvertedToQuery 277 | for (var index = 0; index < _errors.Count; index++) 278 | { 279 | if (_errors[index] is TError) 280 | return true; 281 | } 282 | 283 | return typeof(TError) == typeof(Error); 284 | } 285 | 286 | /// 287 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 288 | public bool HasError([MaybeNullWhen(false)] out TError error) 289 | where TError : IError 290 | { 291 | if (_isSuccess) 292 | { 293 | error = default; 294 | return false; 295 | } 296 | 297 | if (_errors is not null) 298 | // Do not convert to LINQ, this creates unnecessary heap allocations. 299 | // For is the most efficient way to loop. It is the fastest and does not allocate. 300 | // ReSharper disable once ForCanBeConvertedToForeach 301 | // ReSharper disable once LoopCanBeConvertedToQuery 302 | for (var index = 0; index < _errors.Count; index++) 303 | { 304 | if (_errors[index] is not TError tError) 305 | continue; 306 | 307 | error = tError; 308 | return true; 309 | } 310 | 311 | if (typeof(TError) == typeof(Error)) 312 | { 313 | error = (TError)Error.Empty; 314 | return true; 315 | } 316 | 317 | error = default; 318 | return false; 319 | } 320 | 321 | /// Implicitly converts a value to a success . 322 | /// The value to convert into a success result. 323 | /// A new instance of representing a success result with the specified value. 324 | [SuppressMessage("Usage", "CA2225: Operator overloads have named alternates", Justification = "We don't want to expose named alternates in this case.")] 325 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 326 | public static implicit operator Result(TValue value) 327 | { 328 | return new Result(value); 329 | } 330 | 331 | /// Implicitly converts an to a failure . 332 | /// The error to convert into a failure result. 333 | /// A new instance of representing a failure result with the specified error. 334 | [SuppressMessage("Usage", "CA2225: Operator overloads have named alternates", Justification = "We don't want to expose named alternates in this case.")] 335 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 336 | public static implicit operator Result(Error error) 337 | { 338 | return new Result(error); 339 | } 340 | 341 | /// Converts the current to a failure . 342 | /// A new instance of containing the same error as the , if any. 343 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 344 | public Result AsFailure() 345 | { 346 | if (_errors is not null) 347 | return new Result(_errors); 348 | 349 | return Result.FailureResult; 350 | } 351 | 352 | /// Converts the current to a failure . 353 | /// A new instance of containing the same error as the , if any. 354 | /// The type of the value of the failure result. 355 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 356 | public Result AsFailure() 357 | { 358 | if (_errors is not null) 359 | return new Result(_errors); 360 | 361 | return Result.FailureResult; 362 | } 363 | 364 | /// Determines whether two instances are equal. 365 | /// The instance to compare with this instance. 366 | /// true if the specified is equal to this instance; otherwise, false. 367 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 368 | public bool Equals(in Result other) 369 | { 370 | return Equals(_errors, other._errors) && EqualityComparer.Default.Equals(_valueOrDefault, other._valueOrDefault); 371 | } 372 | 373 | /// Determines whether the specified object is equal to this instance. 374 | /// The object to compare with this instance. 375 | /// true if the specified object is equal to this instance; otherwise, false. 376 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 377 | public override bool Equals(object? obj) 378 | { 379 | return obj is Result other && Equals(in other); 380 | } 381 | 382 | /// 383 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 384 | bool IEquatable>.Equals(Result other) 385 | { 386 | return Equals(in other); 387 | } 388 | 389 | /// Returns the hash code for this instance. 390 | /// A 32-bit signed integer hash code. 391 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 392 | public override int GetHashCode() 393 | { 394 | return HashCode.Combine(_errors, _valueOrDefault); 395 | } 396 | 397 | /// Determines whether two instances are equal. 398 | /// The first instance to compare. 399 | /// The second instance to compare. 400 | /// true if the specified instances are equal; otherwise, false. 401 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 402 | public static bool operator ==(in Result left, in Result right) 403 | { 404 | return left.Equals(in right); 405 | } 406 | 407 | /// Determines whether two instances are not equal. 408 | /// The first instance to compare. 409 | /// The second instance to compare. 410 | /// true if the specified instances are not equal; otherwise, false. 411 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 412 | public static bool operator !=(in Result left, in Result right) 413 | { 414 | return !left.Equals(in right); 415 | } 416 | 417 | /// 418 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 419 | public override string ToString() 420 | { 421 | if (_isSuccess) 422 | return StringHelper.GetResultValueString(_valueOrDefault); 423 | 424 | if (_errors is not null && _errors[0].Message.Length > 0) 425 | return StringHelper.GetResultErrorString(_errors[0].Message); 426 | 427 | return $"{nameof(Result)} {{ IsSuccess = False }}"; 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /tests/LightResults.Tests/ActionableResultTests.cs: -------------------------------------------------------------------------------- 1 | #if NET7_0_OR_GREATER 2 | using System.Diagnostics.CodeAnalysis; 3 | using LightResults.Common; 4 | using Shouldly; 5 | 6 | namespace LightResults.Tests; 7 | 8 | public sealed class ActionableResultEdgeTests 9 | { 10 | private readonly struct CustomResult : IActionableResult 11 | { 12 | private readonly Result _inner; 13 | 14 | private CustomResult(Result inner) 15 | { 16 | _inner = inner; 17 | } 18 | 19 | public static CustomResult Success() 20 | { 21 | return new CustomResult(Result.Success()); 22 | } 23 | 24 | public static CustomResult Failure() 25 | { 26 | return new CustomResult(Result.Failure()); 27 | } 28 | 29 | public static CustomResult Failure(string errorMessage) 30 | { 31 | return new CustomResult(Result.Failure(errorMessage)); 32 | } 33 | 34 | public static CustomResult Failure(string errorMessage, (string Key, object? Value) metadata) 35 | { 36 | return new CustomResult(Result.Failure(errorMessage, metadata)); 37 | } 38 | 39 | public static CustomResult Failure(string errorMessage, KeyValuePair metadata) 40 | { 41 | return new CustomResult(Result.Failure(errorMessage, metadata)); 42 | } 43 | 44 | public static CustomResult Failure(string errorMessage, IReadOnlyDictionary metadata) 45 | { 46 | return new CustomResult(Result.Failure(errorMessage, metadata)); 47 | } 48 | 49 | public static CustomResult Failure(Exception? ex) 50 | { 51 | return new CustomResult(Result.Failure(ex)); 52 | } 53 | 54 | public static CustomResult Failure(string errorMessage, Exception? ex) 55 | { 56 | return new CustomResult(Result.Failure(errorMessage, ex)); 57 | } 58 | 59 | public static CustomResult Failure(IError error) 60 | { 61 | return new CustomResult(Result.Failure(error)); 62 | } 63 | 64 | public static CustomResult Failure(IEnumerable errors) 65 | { 66 | return new CustomResult(Result.Failure(errors)); 67 | } 68 | 69 | public static CustomResult Failure(IReadOnlyList errors) 70 | { 71 | return new CustomResult(Result.Failure(errors)); 72 | } 73 | 74 | public IReadOnlyCollection Errors => _inner.Errors; 75 | 76 | public bool IsSuccess() => _inner.IsSuccess(); 77 | 78 | public bool IsFailure() => _inner.IsFailure(); 79 | 80 | public bool IsFailure([MaybeNullWhen(false)] out IError error) => _inner.IsFailure(out error); 81 | 82 | public bool HasError() where TError : IError => _inner.HasError(); 83 | 84 | public bool HasError([MaybeNullWhen(false)] out TError error) where TError : IError => _inner.HasError(out error); 85 | } 86 | 87 | [Fact] 88 | public void CustomActionableResult_Success_ShouldCreateSuccessResult() 89 | { 90 | // Act 91 | var result = CustomResult.Success(); 92 | 93 | // Assert 94 | result.IsSuccess().ShouldBeTrue(); 95 | result.IsFailure().ShouldBeFalse(); 96 | } 97 | 98 | [Fact] 99 | public void CustomActionableResult_Failure_ShouldCreateFailureResult() 100 | { 101 | // Act 102 | var result = CustomResult.Failure(); 103 | 104 | // Assert 105 | result.IsSuccess().ShouldBeFalse(); 106 | result.IsFailure().ShouldBeTrue(); 107 | result.Errors.ShouldHaveSingleItem(); 108 | } 109 | } 110 | #endif 111 | -------------------------------------------------------------------------------- /tests/LightResults.Tests/CustomErrorTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | 3 | namespace LightResults.Tests; 4 | 5 | public sealed class CustomErrorTests 6 | { 7 | [Fact] 8 | public void DefaultConstructor_ShouldCreateEmptyCustomError() 9 | { 10 | // Arrange 11 | var error = new CustomError(); 12 | 13 | // Assert 14 | error.Message.ShouldBeEmpty(); 15 | error.Metadata.ShouldBeEmpty(); 16 | } 17 | 18 | [Fact] 19 | public void ConstructorWithMessage_ShouldCreateErrorWithMessage() 20 | { 21 | // Arrange 22 | const string errorMessage = "Sample error message"; 23 | 24 | // Act 25 | var error = new CustomError(errorMessage); 26 | 27 | // Assert 28 | error.Message.ShouldBe(errorMessage); 29 | error.Metadata.ShouldBeEmpty(); 30 | } 31 | 32 | [Fact] 33 | public void ConstructorWithMessageAndMetadataDictionary_ShouldCreateErrorWithMessageAndMultipleMetadata() 34 | { 35 | // Arrange 36 | const string errorMessage = "Sample error message"; 37 | var metadata = new Dictionary 38 | { 39 | { "Key1", "Value1" }, 40 | { "Key2", 42 }, 41 | }; 42 | 43 | // Act 44 | var error = new CustomError(errorMessage, metadata); 45 | 46 | // Assert 47 | error.Message.ShouldBe(errorMessage); 48 | error.Metadata.Count.ShouldBe(2); 49 | error.Metadata.ShouldBe(metadata); 50 | } 51 | 52 | [Theory] 53 | [InlineData("")] 54 | [InlineData("An unknown error occurred!")] 55 | public void ToString_ShouldReturnStringRepresentation(string errorMessage) 56 | { 57 | // Arrange 58 | var error = new CustomError(errorMessage); 59 | 60 | // Assert 61 | error.ToString().ShouldBe(errorMessage.Length > 0 62 | ? $"CustomError {{ Message = \"{errorMessage}\" }}" 63 | : "CustomError"); 64 | } 65 | 66 | private sealed class CustomError : Error 67 | { 68 | public CustomError() 69 | { 70 | } 71 | 72 | public CustomError(string errorMessage) 73 | : base(errorMessage) 74 | { 75 | } 76 | 77 | public CustomError(string errorMessage, IReadOnlyDictionary metadata) 78 | : base(errorMessage, metadata) 79 | { 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/LightResults.Tests/ErrorTests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | #if NET6_0_OR_GREATER 3 | using System.Diagnostics.CodeAnalysis; 4 | #endif 5 | 6 | namespace LightResults.Tests; 7 | 8 | public sealed class ErrorTests 9 | { 10 | [Fact] 11 | public void DefaultConstructor_ShouldCreateEmptyError() 12 | { 13 | // Arrange 14 | var error = new Error(); 15 | 16 | // Assert 17 | error.Message.ShouldBeEmpty(); 18 | error.Metadata.ShouldBeEmpty(); 19 | } 20 | 21 | [Fact] 22 | public void ConstructorWithMessage_ShouldCreateErrorWithMessage() 23 | { 24 | // Arrange 25 | const string errorMessage = "Sample error message"; 26 | 27 | // Act 28 | var error = new Error(errorMessage); 29 | 30 | // Assert 31 | error.Message.ShouldBe(errorMessage); 32 | error.Metadata.ShouldBeEmpty(); 33 | } 34 | 35 | [Fact] 36 | public void ConstructorWithMessageAndMetadataTuple_ShouldCreateErrorWithMessageAndMetadata() 37 | { 38 | // Arrange 39 | const string errorMessage = "Sample error message"; 40 | var metadata = (Key: "Key1", Value: "Value1"); 41 | 42 | // Act 43 | var error = new Error(errorMessage, metadata); 44 | 45 | // Assert 46 | error.Message.ShouldBe(errorMessage); 47 | error.Metadata.Count.ShouldBe(1); 48 | var firstMetadata = error.Metadata.First(); 49 | firstMetadata.Key.ShouldBe(metadata.Key); 50 | firstMetadata.Value.ShouldBe(metadata.Value); 51 | } 52 | 53 | [Fact] 54 | public void ConstructorWithMessageAndMetadataKeyValuePair_ShouldCreateErrorWithMessageAndMetadata() 55 | { 56 | // Arrange 57 | const string errorMessage = "Sample error message"; 58 | var metadata = new KeyValuePair("Key1", "Value1"); 59 | 60 | // Act 61 | var error = new Error(errorMessage, metadata); 62 | 63 | // Assert 64 | error.Message.ShouldBe(errorMessage); 65 | error.Metadata.Count.ShouldBe(1); 66 | var firstMetadata = error.Metadata.First(); 67 | firstMetadata.Key.ShouldBe(metadata.Key); 68 | firstMetadata.Value.ShouldBe(metadata.Value); 69 | } 70 | 71 | #if NET6_0_OR_GREATER 72 | [Fact] 73 | [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] 74 | public void ConstructorWithMessageAndMetadataIEnumerable_ShouldCreateErrorWithMessageAndMetadata() 75 | { 76 | // Arrange 77 | const string errorMessage = "Sample error message"; 78 | var metadata = new Dictionary 79 | { 80 | { "Key1", "Value1" }, 81 | { "Key2", 42 }, 82 | }.AsEnumerable(); 83 | 84 | // Act 85 | var error = new Error(errorMessage, metadata); 86 | 87 | // Assert 88 | error.Message.ShouldBe(errorMessage); 89 | error.Metadata.Count.ShouldBe(2); 90 | error.Metadata.ShouldBe(metadata); 91 | } 92 | #endif 93 | 94 | [Fact] 95 | public void ConstructorWithMessageAndMetadataDictionary_ShouldCreateErrorWithMessageAndMetadata() 96 | { 97 | // Arrange 98 | const string errorMessage = "Sample error message"; 99 | var metadata = new Dictionary 100 | { 101 | { "Key1", "Value1" }, 102 | { "Key2", 42 }, 103 | }; 104 | 105 | // Act 106 | var error = new Error(errorMessage, metadata); 107 | 108 | // Assert 109 | error.Message.ShouldBe(errorMessage); 110 | error.Metadata.Count.ShouldBe(2); 111 | error.Metadata.ShouldBe(metadata); 112 | } 113 | 114 | [Fact] 115 | public void MessagePropertyInit_ShouldCreateErrorWithMessage() 116 | { 117 | // Arrange 118 | const string errorMessage = "Sample error message"; 119 | var error = new Error 120 | { 121 | Message = errorMessage, 122 | }; 123 | 124 | // Assert 125 | error.Message.ShouldBe(errorMessage); 126 | error.Metadata.ShouldBeEmpty(); 127 | } 128 | 129 | [Fact] 130 | public void MetadataPropertyInit_ShouldCreateErrorWithMetadata() 131 | { 132 | // Arrange 133 | var metadata = new Dictionary 134 | { 135 | { "Key1", "Value1" }, 136 | { "Key2", 42 }, 137 | }; 138 | var error = new Error 139 | { 140 | Metadata = metadata, 141 | }; 142 | 143 | // Assert 144 | error.Message.ShouldBeEmpty(); 145 | error.Metadata.Count.ShouldBe(2); 146 | error.Metadata.ShouldBe(metadata); 147 | } 148 | 149 | [Fact] 150 | public void ConstructorWithException_ShouldCreateErrorWithMessageAndMetadata() 151 | { 152 | // Arrange 153 | var exception = new InvalidOperationException("ex message"); 154 | 155 | // Act 156 | var error = new Error(exception); 157 | 158 | // Assert 159 | error.Message.ShouldBe($"{exception.GetType().Name}: {exception.Message}"); 160 | error.Metadata.Count.ShouldBe(1); 161 | var metadata = error.Metadata.Single(); 162 | metadata.Key.ShouldBe("Exception"); 163 | metadata.Value.ShouldBe(exception); 164 | } 165 | 166 | [Fact] 167 | public void ConstructorWithMessageAndException_ShouldCreateErrorWithMessageAndMetadata() 168 | { 169 | // Arrange 170 | const string errorMessage = "Sample error message"; 171 | var exception = new InvalidOperationException("ex message"); 172 | 173 | // Act 174 | var error = new Error(errorMessage, exception); 175 | 176 | // Assert 177 | error.Message.ShouldBe(errorMessage); 178 | error.Metadata.Count.ShouldBe(1); 179 | var metadata = error.Metadata.Single(); 180 | metadata.Key.ShouldBe("Exception"); 181 | metadata.Value.ShouldBe(exception); 182 | } 183 | 184 | [Theory] 185 | [InlineData("")] 186 | [InlineData("An unknown error occurred!")] 187 | public void ToString_ShouldReturnStringRepresentation(string errorMessage) 188 | { 189 | // Arrange 190 | var error = new Error(errorMessage); 191 | 192 | // Assert 193 | error.ToString() 194 | .ShouldBe(errorMessage.Length > 0 ? $"Error {{ Message = \"{errorMessage}\" }}" : "Error"); 195 | } 196 | 197 | [Fact] 198 | public void Equals_Error_ShouldReturnTrueForEqualErrors() 199 | { 200 | // Arrange 201 | var error1 = new Error("error", ("Key", 1)); 202 | var error2 = new Error("error", ("Key", 1)); 203 | 204 | // Assert 205 | error1.Equals(error2) 206 | .ShouldBeTrue(); 207 | } 208 | 209 | [Fact] 210 | public void Equals_Error_ShouldReturnFalseForUnequalErrors() 211 | { 212 | // Arrange 213 | var error1 = new Error("error", ("Key", 1)); 214 | var error2 = new Error("error", ("Key", 2)); 215 | 216 | // Assert 217 | error1.Equals(error2) 218 | .ShouldBeFalse(); 219 | } 220 | 221 | [Fact] 222 | public void Equals_Object_ShouldReturnTrueForEqualErrors() 223 | { 224 | // Arrange 225 | var error1 = new Error("error", ("Key", 1)); 226 | var error2 = new Error("error", ("Key", 1)); 227 | 228 | // Assert 229 | error1.Equals((object)error2) 230 | .ShouldBeTrue(); 231 | } 232 | 233 | [Fact] 234 | public void Equals_Object_ShouldReturnFalseForUnequalErrors() 235 | { 236 | // Arrange 237 | var error1 = new Error("error", ("Key", 1)); 238 | var error2 = new Error("error", ("Key", 2)); 239 | 240 | // Assert 241 | error1.Equals((object)error2) 242 | .ShouldBeFalse(); 243 | } 244 | 245 | [Fact] 246 | public void GetHashCode_ShouldReturnSameHashCodeForEqualErrors() 247 | { 248 | // Arrange 249 | var error1 = new Error("error", ("Key", 1)); 250 | var error2 = new Error("error", ("Key", 1)); 251 | 252 | // Assert 253 | error1.GetHashCode() 254 | .ShouldBe(error2.GetHashCode()); 255 | } 256 | 257 | [Fact] 258 | public void op_Equality_Error_ShouldReturnTrueForEqualErrors() 259 | { 260 | // Arrange 261 | var error1 = new Error("error", ("Key", 1)); 262 | var error2 = new Error("error", ("Key", 1)); 263 | 264 | // Assert 265 | (error1 == error2).ShouldBeTrue(); 266 | } 267 | 268 | [Fact] 269 | public void op_Equality_Error_ShouldReturnFalseForUnequalErrors() 270 | { 271 | // Arrange 272 | var error1 = new Error("error", ("Key", 1)); 273 | var error2 = new Error("error", ("Key", 2)); 274 | 275 | // Assert 276 | (error1 == error2).ShouldBeFalse(); 277 | } 278 | 279 | [Fact] 280 | public void op_Inequality_Error_ShouldReturnFalseForEqualErrors() 281 | { 282 | // Arrange 283 | var error1 = new Error("error", ("Key", 1)); 284 | var error2 = new Error("error", ("Key", 1)); 285 | 286 | // Assert 287 | (error1 != error2).ShouldBeFalse(); 288 | } 289 | 290 | [Fact] 291 | public void op_Inequality_Error_ShouldReturnTrueForUnequalErrors() 292 | { 293 | // Arrange 294 | var error1 = new Error("error", ("Key", 1)); 295 | var error2 = new Error("error", ("Key", 2)); 296 | 297 | // Assert 298 | (error1 != error2).ShouldBeTrue(); 299 | } 300 | 301 | [Fact] 302 | public void ExceptionProperty_ShouldReturnExceptionWhenMetadataContainsException() 303 | { 304 | // Arrange 305 | var exception = new InvalidOperationException(); 306 | var error = new Error("", ("Exception", exception)); 307 | 308 | // Assert 309 | error.Exception.ShouldBe(exception); 310 | } 311 | 312 | [Fact] 313 | public void ExceptionProperty_ShouldReturnNullWhenMetadataDoesNotContainException() 314 | { 315 | // Arrange 316 | var error = new Error(); 317 | 318 | // Assert 319 | error.Exception.ShouldBeNull(); 320 | } 321 | 322 | [Fact] 323 | public void ExceptionProperty_ShouldReturnNullWhenMetadataIsNotException() 324 | { 325 | // Arrange 326 | var error = new Error("", ("Exception", "not exception")); 327 | 328 | // Assert 329 | error.Exception.ShouldBeNull(); 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /tests/LightResults.Tests/LightResults.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net481;net6.0;net7.0;net8.0;net9.0 5 | enable 6 | enable 7 | latest 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/Benchmarks.A_LightResults.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace LightResults.ComparisonBenchmarks; 4 | 5 | // ReSharper disable RedundantTypeArgumentsOfMethod 6 | public partial class Benchmarks 7 | { 8 | private static readonly Error LightResultsErrorWithErrorMessage = new(ErrorMessage); 9 | private static readonly Result LightResultsResultOk = Result.Success(); 10 | private static readonly Result LightResultsResultFail = Result.Failure(); 11 | private static readonly Result LightResultsResultFailWithErrorMessage = Result.Failure(LightResultsErrorWithErrorMessage); 12 | private static readonly Result LightResultsResultTValueOk = Result.Success(ResultValue); 13 | private static readonly Result LightResultsResultTValueFail = Result.Failure(); 14 | private static readonly Result LightResultsResultTValueFailWithErrorMessage = Result.Failure(LightResultsErrorWithErrorMessage); 15 | 16 | [Benchmark(Baseline = true)] 17 | [BenchmarkCategory("A01: Returning a successful result")] 18 | public void A_LightResults_Result_Ok() 19 | { 20 | for (var iteration = 0; iteration < Iterations; iteration++) 21 | _ = Result.Success(); 22 | } 23 | 24 | [Benchmark(Baseline = true)] 25 | [BenchmarkCategory("B01: String representation of a successful result")] 26 | public void A_LightResults_Result_Ok_ToString() 27 | { 28 | for (var iteration = 0; iteration < Iterations; iteration++) 29 | _ = LightResultsResultOk.ToString(); 30 | } 31 | 32 | [Benchmark(Baseline = true)] 33 | [BenchmarkCategory("A02: Returning a successful value result")] 34 | public void A_LightResults_Result_OkTValue() 35 | { 36 | for (var iteration = 0; iteration < Iterations; iteration++) 37 | _ = Result.Success(ResultValue); 38 | } 39 | 40 | [Benchmark(Baseline = true)] 41 | [BenchmarkCategory("B02: String representation of a successful value result")] 42 | public void A_LightResults_Result_OkTValue_ToString() 43 | { 44 | for (var iteration = 0; iteration < Iterations; iteration++) 45 | _ = LightResultsResultTValueOk.ToString(); 46 | } 47 | 48 | [Benchmark(Baseline = true)] 49 | [BenchmarkCategory("A03: Returning a failed result")] 50 | public void A_LightResults_Result_Fail() 51 | { 52 | for (var iteration = 0; iteration < Iterations; iteration++) 53 | _ = Result.Failure(); 54 | } 55 | 56 | [Benchmark(Baseline = true)] 57 | [BenchmarkCategory("B03: String representation of a failed result")] 58 | public void A_LightResults_Result_Fail_ToString() 59 | { 60 | for (var iteration = 0; iteration < Iterations; iteration++) 61 | _ = LightResultsResultFail.ToString(); 62 | } 63 | 64 | [Benchmark(Baseline = true)] 65 | [BenchmarkCategory("A04: Returning a failed result with an error message")] 66 | public void A_LightResults_Result_Fail_WithErrorMessage() 67 | { 68 | for (var iteration = 0; iteration < Iterations; iteration++) 69 | _ = Result.Failure(LightResultsErrorWithErrorMessage); 70 | } 71 | 72 | [Benchmark(Baseline = true)] 73 | [BenchmarkCategory("B04: String representation of a failed result with an error message")] 74 | public void A_LightResults_Result_Fail_WithErrorMessage_ToString() 75 | { 76 | for (var iteration = 0; iteration < Iterations; iteration++) 77 | _ = LightResultsResultFailWithErrorMessage.ToString(); 78 | } 79 | 80 | [Benchmark(Baseline = true)] 81 | [BenchmarkCategory("A05: Returning a failed value result")] 82 | public void A_LightResults_Result_FailTValue() 83 | { 84 | for (var iteration = 0; iteration < Iterations; iteration++) 85 | _ = Result.Failure(); 86 | } 87 | 88 | [Benchmark(Baseline = true)] 89 | [BenchmarkCategory("B05: String representation of a failed value result")] 90 | public void A_LightResults_Result_FailTValue_ToString() 91 | { 92 | for (var iteration = 0; iteration < Iterations; iteration++) 93 | _ = LightResultsResultTValueFail.ToString(); 94 | } 95 | 96 | [Benchmark(Baseline = true)] 97 | [BenchmarkCategory("A06: Returning a failed value result with an error message")] 98 | public void A_LightResults_Result_FailTValue_WithErrorMessage() 99 | { 100 | for (var iteration = 0; iteration < Iterations; iteration++) 101 | _ = Result.Failure(LightResultsErrorWithErrorMessage); 102 | } 103 | 104 | [Benchmark(Baseline = true)] 105 | [BenchmarkCategory("B06: String representation of a failed value result with an error message")] 106 | public void A_LightResults_Result_FailTValue_WithErrorMessage_ToString() 107 | { 108 | for (var iteration = 0; iteration < Iterations; iteration++) 109 | _ = LightResultsResultTValueFailWithErrorMessage.ToString(); 110 | } 111 | 112 | [Benchmark(Baseline = true)] 113 | [BenchmarkCategory("C01: Determining if a result is successful")] 114 | public void A_LightResults_Result_IsSuccess() 115 | { 116 | for (var iteration = 0; iteration < Iterations; iteration++) 117 | _ = LightResultsResultOk.IsSuccess(); 118 | } 119 | 120 | [Benchmark(Baseline = true)] 121 | [BenchmarkCategory("C02: Retrieving the value")] 122 | public void A_LightResults_Result_Value() 123 | { 124 | for (var iteration = 0; iteration < Iterations; iteration++) 125 | _ = LightResultsResultTValueOk.IsSuccess(out _); 126 | } 127 | 128 | [Benchmark(Baseline = true)] 129 | [BenchmarkCategory("C03: Determining if a result is failed")] 130 | public void A_LightResults_Result_IsFailed() 131 | { 132 | for (var iteration = 0; iteration < Iterations; iteration++) 133 | _ = LightResultsResultFail.IsFailure(); 134 | } 135 | 136 | [Benchmark(Baseline = true)] 137 | [BenchmarkCategory("C04: Determining if a result contains a specific error")] 138 | public void A_LightResults_Result_HasError() 139 | { 140 | for (var iteration = 0; iteration < Iterations; iteration++) 141 | _ = LightResultsResultFailWithErrorMessage.HasError(); 142 | } 143 | 144 | [Benchmark(Baseline = true)] 145 | [BenchmarkCategory("C05: Retrieving the first error")] 146 | public void A_LightResults_Result_FirstError() 147 | { 148 | for (var iteration = 0; iteration < Iterations; iteration++) 149 | _ = LightResultsResultFailWithErrorMessage.IsFailure(out _); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/Benchmarks.B_FluentResults.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace LightResults.ComparisonBenchmarks; 4 | 5 | // ReSharper disable RedundantTypeArgumentsOfMethod 6 | public partial class Benchmarks 7 | { 8 | private static readonly FluentResults.Error FluentResultsErrorWithErrorMessage = new(ErrorMessage); 9 | private static readonly FluentResults.Result FluentResultsResultOk = FluentResults.Result.Ok(); 10 | private static readonly FluentResults.Result FluentResultsResultFail = FluentResults.Result.Fail(""); 11 | private static readonly FluentResults.Result FluentResultsResultFailWithErrorMessage = FluentResults.Result.Fail(FluentResultsErrorWithErrorMessage); 12 | private static readonly FluentResults.Result FluentResultsResultTValueOk = FluentResults.Result.Ok(ResultValue); 13 | private static readonly FluentResults.Result FluentResultsResultTValueFail = FluentResults.Result.Fail(""); 14 | 15 | private static readonly FluentResults.Result FluentResultsResultTValueFailWithErrorMessage = 16 | FluentResults.Result.Fail(FluentResultsErrorWithErrorMessage); 17 | 18 | [Benchmark] 19 | [BenchmarkCategory("A01: Returning a successful result")] 20 | public void B_FluentResults_Result_Ok() 21 | { 22 | for (var iteration = 0; iteration < Iterations; iteration++) 23 | _ = FluentResults.Result.Ok(); 24 | } 25 | 26 | [Benchmark] 27 | [BenchmarkCategory("B01: String representation of a successful result")] 28 | public void B_FluentResults_Result_Ok_ToString() 29 | { 30 | for (var iteration = 0; iteration < Iterations; iteration++) 31 | _ = FluentResultsResultOk.ToString(); 32 | } 33 | 34 | [Benchmark] 35 | [BenchmarkCategory("A02: Returning a successful value result")] 36 | public void B_FluentResults_Result_OkTValue() 37 | { 38 | for (var iteration = 0; iteration < Iterations; iteration++) 39 | _ = FluentResults.Result.Ok(ResultValue); 40 | } 41 | 42 | [Benchmark] 43 | [BenchmarkCategory("B02: String representation of a successful value result")] 44 | public void B_FluentResults_Result_OkTValue_ToString() 45 | { 46 | for (var iteration = 0; iteration < Iterations; iteration++) 47 | _ = FluentResultsResultTValueOk.ToString(); 48 | } 49 | 50 | [Benchmark] 51 | [BenchmarkCategory("A03: Returning a failed result")] 52 | public void B_FluentResults_Result_Fail() 53 | { 54 | for (var iteration = 0; iteration < Iterations; iteration++) 55 | _ = FluentResults.Result.Fail(""); 56 | } 57 | 58 | [Benchmark] 59 | [BenchmarkCategory("B03: String representation of a failed result")] 60 | public void B_FluentResults_Result_Fail_ToString() 61 | { 62 | for (var iteration = 0; iteration < Iterations; iteration++) 63 | _ = FluentResultsResultFail.ToString(); 64 | } 65 | 66 | [Benchmark] 67 | [BenchmarkCategory("A04: Returning a failed result with an error message")] 68 | public void B_FluentResults_Result_Fail_WithErrorMessage() 69 | { 70 | for (var iteration = 0; iteration < Iterations; iteration++) 71 | _ = FluentResults.Result.Fail(FluentResultsErrorWithErrorMessage); 72 | } 73 | 74 | [Benchmark] 75 | [BenchmarkCategory("B04: String representation of a failed result with an error message")] 76 | public void B_FluentResults_Result_Fail_WithErrorMessage_ToString() 77 | { 78 | for (var iteration = 0; iteration < Iterations; iteration++) 79 | _ = FluentResultsResultFailWithErrorMessage.ToString(); 80 | } 81 | 82 | [Benchmark] 83 | [BenchmarkCategory("A05: Returning a failed value result")] 84 | public void B_FluentResults_Result_FailTValue() 85 | { 86 | for (var iteration = 0; iteration < Iterations; iteration++) 87 | _ = FluentResults.Result.Fail(""); 88 | } 89 | 90 | [Benchmark] 91 | [BenchmarkCategory("B05: String representation of a failed value result")] 92 | public void B_FluentResults_Result_FailTValue_ToString() 93 | { 94 | for (var iteration = 0; iteration < Iterations; iteration++) 95 | _ = FluentResultsResultTValueFail.ToString(); 96 | } 97 | 98 | [Benchmark] 99 | [BenchmarkCategory("A06: Returning a failed value result with an error message")] 100 | public void B_FluentResults_Result_FailTValue_WithErrorMessage() 101 | { 102 | for (var iteration = 0; iteration < Iterations; iteration++) 103 | _ = FluentResults.Result.Fail(FluentResultsErrorWithErrorMessage); 104 | } 105 | 106 | [Benchmark] 107 | [BenchmarkCategory("B06: String representation of a failed value result with an error message")] 108 | public void B_FluentResults_Result_FailTValue_WithErrorMessage_ToString() 109 | { 110 | for (var iteration = 0; iteration < Iterations; iteration++) 111 | _ = FluentResultsResultTValueFailWithErrorMessage.ToString(); 112 | } 113 | 114 | [Benchmark] 115 | [BenchmarkCategory("C01: Determining if a result is successful")] 116 | public void B_FluentResults_Result_IsSuccess() 117 | { 118 | for (var iteration = 0; iteration < Iterations; iteration++) 119 | _ = FluentResultsResultOk.IsSuccess; 120 | } 121 | 122 | [Benchmark] 123 | [BenchmarkCategory("C02: Retrieving the value")] 124 | public void B_FluentResults_Result_Value() 125 | { 126 | for (var iteration = 0; iteration < Iterations; iteration++) 127 | _ = FluentResultsResultTValueOk.Value; 128 | } 129 | 130 | [Benchmark] 131 | [BenchmarkCategory("C03: Determining if a result is failed")] 132 | public void B_FluentResults_Result_IsFailed() 133 | { 134 | for (var iteration = 0; iteration < Iterations; iteration++) 135 | _ = FluentResultsResultFail.IsFailed; 136 | } 137 | 138 | [Benchmark] 139 | [BenchmarkCategory("C04: Determining if a result contains a specific error")] 140 | public void B_FluentResults_Result_HasError() 141 | { 142 | for (var iteration = 0; iteration < Iterations; iteration++) 143 | _ = FluentResultsResultFailWithErrorMessage.HasError(); 144 | } 145 | 146 | [Benchmark] 147 | [BenchmarkCategory("C05: Retrieving the first error")] 148 | public void B_FluentResults_Result_FirstError() 149 | { 150 | for (var iteration = 0; iteration < Iterations; iteration++) 151 | _ = FluentResultsResultFailWithErrorMessage.Errors[0]; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/Benchmarks.C_ArdalisResult.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace LightResults.ComparisonBenchmarks; 4 | 5 | // ReSharper disable RedundantTypeArgumentsOfMethod 6 | public partial class Benchmarks 7 | { 8 | private static readonly Ardalis.Result.Result ArdalisResultResultOk = Ardalis.Result.Result.Success(); 9 | private static readonly Ardalis.Result.Result ArdalisResultResultFail = Ardalis.Result.Result.Error(); 10 | private static readonly Ardalis.Result.Result ArdalisResultResultFailWithErrorMessage = Ardalis.Result.Result.Error(ErrorMessage); 11 | private static readonly Ardalis.Result.Result ArdalisResultResultTValueOk = Ardalis.Result.Result.Success(ResultValue); 12 | private static readonly Ardalis.Result.Result ArdalisResultResultTValueFail = Ardalis.Result.Result.Error(); 13 | private static readonly Ardalis.Result.Result ArdalisResultResultTValueFailWithErrorMessage = Ardalis.Result.Result.Error(ErrorMessage); 14 | 15 | [Benchmark] 16 | [BenchmarkCategory("A01: Returning a successful result")] 17 | public void C_ArdalisResult_Result_Ok() 18 | { 19 | for (var iteration = 0; iteration < Iterations; iteration++) 20 | _ = Ardalis.Result.Result.Success(); 21 | } 22 | 23 | [Benchmark] 24 | [BenchmarkCategory("B01: String representation of a successful result")] 25 | public void C_ArdalisResult_Result_Ok_ToString() 26 | { 27 | for (var iteration = 0; iteration < Iterations; iteration++) 28 | _ = ArdalisResultResultOk.ToString(); 29 | } 30 | 31 | [Benchmark] 32 | [BenchmarkCategory("A02: Returning a successful value result")] 33 | public void C_ArdalisResult_Result_OkTValue() 34 | { 35 | for (var iteration = 0; iteration < Iterations; iteration++) 36 | _ = Ardalis.Result.Result.Success(ResultValue); 37 | } 38 | 39 | [Benchmark] 40 | [BenchmarkCategory("B02: String representation of a successful value result")] 41 | public void C_ArdalisResult_Result_OkTValue_ToString() 42 | { 43 | for (var iteration = 0; iteration < Iterations; iteration++) 44 | _ = ArdalisResultResultTValueOk.ToString(); 45 | } 46 | 47 | [Benchmark] 48 | [BenchmarkCategory("A03: Returning a failed result")] 49 | public void C_ArdalisResult_Result_Fail() 50 | { 51 | for (var iteration = 0; iteration < Iterations; iteration++) 52 | _ = Ardalis.Result.Result.Error(); 53 | } 54 | 55 | [Benchmark] 56 | [BenchmarkCategory("B03: String representation of a failed result")] 57 | public void C_ArdalisResult_Result_Fail_ToString() 58 | { 59 | for (var iteration = 0; iteration < Iterations; iteration++) 60 | _ = ArdalisResultResultFail.ToString(); 61 | } 62 | 63 | [Benchmark] 64 | [BenchmarkCategory("A04: Returning a failed result with an error message")] 65 | public void C_ArdalisResult_Result_Fail_WithErrorMessage() 66 | { 67 | for (var iteration = 0; iteration < Iterations; iteration++) 68 | _ = Ardalis.Result.Result.Error(ErrorMessage); 69 | } 70 | 71 | [Benchmark] 72 | [BenchmarkCategory("B04: String representation of a failed result with an error message")] 73 | public void C_ArdalisResult_Result_Fail_WithErrorMessage_ToString() 74 | { 75 | for (var iteration = 0; iteration < Iterations; iteration++) 76 | _ = ArdalisResultResultFailWithErrorMessage.ToString(); 77 | } 78 | 79 | [Benchmark] 80 | [BenchmarkCategory("A05: Returning a failed value result")] 81 | public void C_ArdalisResult_Result_FailTValue() 82 | { 83 | for (var iteration = 0; iteration < Iterations; iteration++) 84 | _ = Ardalis.Result.Result.Error(); 85 | } 86 | 87 | [Benchmark] 88 | [BenchmarkCategory("B05: String representation of a failed value result")] 89 | public void C_ArdalisResult_Result_FailTValue_ToString() 90 | { 91 | for (var iteration = 0; iteration < Iterations; iteration++) 92 | _ = ArdalisResultResultTValueFail.ToString(); 93 | } 94 | 95 | [Benchmark] 96 | [BenchmarkCategory("A06: Returning a failed value result with an error message")] 97 | public void C_ArdalisResult_Result_FailTValue_WithErrorMessage() 98 | { 99 | for (var iteration = 0; iteration < Iterations; iteration++) 100 | _ = Ardalis.Result.Result.Error(ErrorMessage); 101 | } 102 | 103 | [Benchmark] 104 | [BenchmarkCategory("B06: String representation of a failed value result with an error message")] 105 | public void C_ArdalisResult_Result_FailTValue_WithErrorMessage_ToString() 106 | { 107 | for (var iteration = 0; iteration < Iterations; iteration++) 108 | _ = ArdalisResultResultTValueFailWithErrorMessage.ToString(); 109 | } 110 | 111 | [Benchmark] 112 | [BenchmarkCategory("C01: Determining if a result is successful")] 113 | public void C_ArdalisResult_Result_IsSuccess() 114 | { 115 | for (var iteration = 0; iteration < Iterations; iteration++) 116 | _ = ArdalisResultResultOk.IsSuccess; 117 | } 118 | 119 | [Benchmark] 120 | [BenchmarkCategory("C02: Retrieving the value")] 121 | public void C_ArdalisResult_Result_Value() 122 | { 123 | for (var iteration = 0; iteration < Iterations; iteration++) 124 | _ = ArdalisResultResultTValueOk.Value; 125 | } 126 | 127 | [Benchmark] 128 | [BenchmarkCategory("C03: Determining if a result is failed")] 129 | public void C_ArdalisResult_Result_IsFailed() 130 | { 131 | for (var iteration = 0; iteration < Iterations; iteration++) 132 | _ = !ArdalisResultResultFail.IsSuccess; 133 | } 134 | 135 | [Benchmark] 136 | [BenchmarkCategory("C04: Determining if a result contains a specific error")] 137 | public void C_ArdalisResult_Result_HasError() 138 | { 139 | for (var iteration = 0; iteration < Iterations; iteration++) 140 | _ = ArdalisResultResultFailWithErrorMessage.Errors.Any(errorMessage => errorMessage.Equals(ErrorMessage)); 141 | } 142 | 143 | [Benchmark] 144 | [BenchmarkCategory("C05: Retrieving the first error")] 145 | public void C_ArdalisResult_Result_FirstError() 146 | { 147 | for (var iteration = 0; iteration < Iterations; iteration++) 148 | _ = ArdalisResultResultFailWithErrorMessage.Errors.First(); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/Benchmarks.D_SimpleResults.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using SimpleResults; 3 | 4 | namespace LightResults.ComparisonBenchmarks; 5 | 6 | // ReSharper disable RedundantTypeArgumentsOfMethod 7 | public partial class Benchmarks 8 | { 9 | private static readonly SimpleResults.Result SimpleResultsResultOk = SimpleResults.Result.Success(); 10 | private static readonly SimpleResults.Result SimpleResultsResultFail = SimpleResults.Result.Failure(); 11 | private static readonly SimpleResults.Result SimpleResultsResultFailWithErrorMessage = SimpleResults.Result.Failure(ErrorMessage); 12 | private static readonly SimpleResults.Result SimpleResultsResultTValueOk = SimpleResults.Result.Success(ResultValue); 13 | private static readonly SimpleResults.Result SimpleResultsResultTValueFail = SimpleResults.Result.Failure(); 14 | private static readonly SimpleResults.Result SimpleResultsResultTValueFailWithErrorMessage = SimpleResults.Result.Failure(ErrorMessage); 15 | 16 | [Benchmark] 17 | [BenchmarkCategory("A01: Returning a successful result")] 18 | public void D_SimpleResults_Result_Ok() 19 | { 20 | for (var iteration = 0; iteration < Iterations; iteration++) 21 | _ = SimpleResults.Result.Success(); 22 | } 23 | 24 | [Benchmark] 25 | [BenchmarkCategory("B01: String representation of a successful result")] 26 | public void D_SimpleResults_Result_Ok_ToString() 27 | { 28 | for (var iteration = 0; iteration < Iterations; iteration++) 29 | _ = SimpleResultsResultOk.ToString(); 30 | } 31 | 32 | [Benchmark] 33 | [BenchmarkCategory("A02: Returning a successful value result")] 34 | public void D_SimpleResults_Result_OkTValue() 35 | { 36 | for (var iteration = 0; iteration < Iterations; iteration++) 37 | _ = SimpleResults.Result.Success(ResultValue); 38 | } 39 | 40 | [Benchmark] 41 | [BenchmarkCategory("B02: String representation of a successful value result")] 42 | public void D_SimpleResults_Result_OkTValue_ToString() 43 | { 44 | for (var iteration = 0; iteration < Iterations; iteration++) 45 | _ = SimpleResultsResultTValueOk.ToString(); 46 | } 47 | 48 | [Benchmark] 49 | [BenchmarkCategory("A03: Returning a failed result")] 50 | public void D_SimpleResults_Result_Fail() 51 | { 52 | for (var iteration = 0; iteration < Iterations; iteration++) 53 | _ = SimpleResults.Result.Failure(); 54 | } 55 | 56 | [Benchmark] 57 | [BenchmarkCategory("B03: String representation of a failed result")] 58 | public void D_SimpleResults_Result_Fail_ToString() 59 | { 60 | for (var iteration = 0; iteration < Iterations; iteration++) 61 | _ = SimpleResultsResultFail.ToString(); 62 | } 63 | 64 | [Benchmark] 65 | [BenchmarkCategory("A04: Returning a failed result with an error message")] 66 | public void D_SimpleResults_Result_Fail_WithErrorMessage() 67 | { 68 | for (var iteration = 0; iteration < Iterations; iteration++) 69 | _ = SimpleResults.Result.Failure(ErrorMessage); 70 | } 71 | 72 | [Benchmark] 73 | [BenchmarkCategory("B04: String representation of a failed result with an error message")] 74 | public void D_SimpleResults_Result_Fail_WithErrorMessage_ToString() 75 | { 76 | for (var iteration = 0; iteration < Iterations; iteration++) 77 | _ = SimpleResultsResultFailWithErrorMessage.ToString(); 78 | } 79 | 80 | [Benchmark] 81 | [BenchmarkCategory("A05: Returning a failed value result")] 82 | public void D_SimpleResults_Result_FailTValue() 83 | { 84 | for (var iteration = 0; iteration < Iterations; iteration++) 85 | _ = (SimpleResults.Result)SimpleResults.Result.Failure(); 86 | } 87 | 88 | [Benchmark] 89 | [BenchmarkCategory("B05: String representation of a failed value result")] 90 | public void D_SimpleResults_Result_FailTValue_ToString() 91 | { 92 | for (var iteration = 0; iteration < Iterations; iteration++) 93 | _ = SimpleResultsResultTValueFail.ToString(); 94 | } 95 | 96 | [Benchmark] 97 | [BenchmarkCategory("A06: Returning a failed value result with an error message")] 98 | public void D_SimpleResults_Result_FailTValue_WithErrorMessage() 99 | { 100 | for (var iteration = 0; iteration < Iterations; iteration++) 101 | _ = (SimpleResults.Result)SimpleResults.Result.Failure(ErrorMessage); 102 | } 103 | 104 | [Benchmark] 105 | [BenchmarkCategory("B06: String representation of a failed value result with an error message")] 106 | public void D_SimpleResults_Result_FailTValue_WithErrorMessage_ToString() 107 | { 108 | for (var iteration = 0; iteration < Iterations; iteration++) 109 | _ = SimpleResultsResultTValueFailWithErrorMessage.ToString(); 110 | } 111 | 112 | [Benchmark] 113 | [BenchmarkCategory("C01: Determining if a result is successful")] 114 | public void D_SimpleResults_Result_IsSuccess() 115 | { 116 | for (var iteration = 0; iteration < Iterations; iteration++) 117 | _ = SimpleResultsResultOk.IsSuccess; 118 | } 119 | 120 | [Benchmark] 121 | [BenchmarkCategory("C02: Retrieving the value")] 122 | public void D_SimpleResults_Result_Value() 123 | { 124 | for (var iteration = 0; iteration < Iterations; iteration++) 125 | _ = SimpleResultsResultTValueOk.Data; 126 | } 127 | 128 | [Benchmark] 129 | [BenchmarkCategory("C03: Determining if a result is failed")] 130 | public void D_SimpleResults_Result_IsFailed() 131 | { 132 | for (var iteration = 0; iteration < Iterations; iteration++) 133 | _ = SimpleResultsResultFail.IsFailed; 134 | } 135 | 136 | [Benchmark] 137 | [BenchmarkCategory("C04: Determining if a result contains a specific error")] 138 | public void D_SimpleResults_Result_HasError() 139 | { 140 | for (var iteration = 0; iteration < Iterations; iteration++) 141 | _ = SimpleResultsResultFailWithErrorMessage.Status == ResultStatus.Failure; 142 | } 143 | 144 | [Benchmark] 145 | [BenchmarkCategory("C05: Retrieving the first error")] 146 | public void D_SimpleResults_Result_FirstError() 147 | { 148 | for (var iteration = 0; iteration < Iterations; iteration++) 149 | _ = SimpleResultsResultFailWithErrorMessage.Errors.FirstOrDefault(); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/Benchmarks.E_Rascal.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Rascal; 3 | using Rascal.Errors; 4 | 5 | namespace LightResults.ComparisonBenchmarks; 6 | 7 | // ReSharper disable RedundantTypeArgumentsOfMethod 8 | // ReSharper disable RedundantNameQualifier 9 | public partial class Benchmarks 10 | { 11 | private static readonly Rascal.Error RascalError = new Rascal.Errors.StringError(""); 12 | private static readonly Rascal.Error RascalErrorWithErrorMessage = new Rascal.Errors.StringError(ErrorMessage); 13 | private static readonly Rascal.Result RascalResultOk = Prelude.Ok(true); 14 | private static readonly Rascal.Result RascalResultFail = Prelude.Err(RascalError); 15 | private static readonly Rascal.Result RascalResultFailWithErrorMessage = Prelude.Err(RascalErrorWithErrorMessage); 16 | private static readonly Rascal.Result RascalResultTValueOk = Prelude.Ok(ResultValue); 17 | private static readonly Rascal.Result RascalResultTValueFail = Prelude.Err(RascalError); 18 | private static readonly Rascal.Result RascalResultTValueFailWithErrorMessage = Prelude.Err(RascalErrorWithErrorMessage); 19 | 20 | [Benchmark] 21 | [BenchmarkCategory("A01: Returning a successful result")] 22 | public void E_Rascal_Result_Ok() 23 | { 24 | for (var iteration = 0; iteration < Iterations; iteration++) 25 | _ = Prelude.Ok(true); 26 | } 27 | 28 | [Benchmark] 29 | [BenchmarkCategory("B01: String representation of a successful result")] 30 | public void E_Rascal_Result_Ok_ToString() 31 | { 32 | for (var iteration = 0; iteration < Iterations; iteration++) 33 | _ = RascalResultOk.ToString(); 34 | } 35 | 36 | [Benchmark] 37 | [BenchmarkCategory("A02: Returning a successful value result")] 38 | public void E_Rascal_Result_OkTValue() 39 | { 40 | for (var iteration = 0; iteration < Iterations; iteration++) 41 | _ = Prelude.Ok(ResultValue); 42 | } 43 | 44 | [Benchmark] 45 | [BenchmarkCategory("B02: String representation of a successful value result")] 46 | public void E_Rascal_Result_OkTValue_ToString() 47 | { 48 | for (var iteration = 0; iteration < Iterations; iteration++) 49 | _ = RascalResultTValueOk.ToString(); 50 | } 51 | 52 | [Benchmark] 53 | [BenchmarkCategory("A03: Returning a failed result")] 54 | public void E_Rascal_Result_Fail() 55 | { 56 | for (var iteration = 0; iteration < Iterations; iteration++) 57 | _ = Prelude.Err(""); 58 | } 59 | 60 | [Benchmark] 61 | [BenchmarkCategory("B03: String representation of a failed result")] 62 | public void E_Rascal_Result_Fail_ToString() 63 | { 64 | for (var iteration = 0; iteration < Iterations; iteration++) 65 | _ = RascalResultFail.ToString(); 66 | } 67 | 68 | [Benchmark] 69 | [BenchmarkCategory("A04: Returning a failed result with an error message")] 70 | public void E_Rascal_Result_Fail_WithErrorMessage() 71 | { 72 | for (var iteration = 0; iteration < Iterations; iteration++) 73 | _ = Prelude.Err(RascalErrorWithErrorMessage); 74 | } 75 | 76 | [Benchmark] 77 | [BenchmarkCategory("B04: String representation of a failed result with an error message")] 78 | public void E_Rascal_Result_Fail_WithErrorMessage_ToString() 79 | { 80 | for (var iteration = 0; iteration < Iterations; iteration++) 81 | _ = RascalResultFailWithErrorMessage.ToString(); 82 | } 83 | 84 | [Benchmark] 85 | [BenchmarkCategory("A05: Returning a failed value result")] 86 | public void E_Rascal_Result_FailTValue() 87 | { 88 | for (var iteration = 0; iteration < Iterations; iteration++) 89 | _ = Prelude.Err(""); 90 | } 91 | 92 | [Benchmark] 93 | [BenchmarkCategory("B05: String representation of a failed value result")] 94 | public void E_Rascal_Result_FailTValue_ToString() 95 | { 96 | for (var iteration = 0; iteration < Iterations; iteration++) 97 | _ = RascalResultTValueFail.ToString(); 98 | } 99 | 100 | [Benchmark] 101 | [BenchmarkCategory("A06: Returning a failed value result with an error message")] 102 | public void E_Rascal_Result_FailTValue_WithErrorMessage() 103 | { 104 | for (var iteration = 0; iteration < Iterations; iteration++) 105 | _ = Prelude.Err(RascalErrorWithErrorMessage); 106 | } 107 | 108 | [Benchmark] 109 | [BenchmarkCategory("B06: String representation of a failed value result with an error message")] 110 | public void E_Rascal_Result_FailTValue_WithErrorMessage_ToString() 111 | { 112 | for (var iteration = 0; iteration < Iterations; iteration++) 113 | _ = RascalResultTValueFailWithErrorMessage.ToString(); 114 | } 115 | 116 | [Benchmark] 117 | [BenchmarkCategory("C01: Determining if a result is successful")] 118 | public void E_Rascal_Result_IsSuccess() 119 | { 120 | for (var iteration = 0; iteration < Iterations; iteration++) 121 | _ = RascalResultOk.IsOk; 122 | } 123 | 124 | [Benchmark] 125 | [BenchmarkCategory("C02: Retrieving the value")] 126 | public void E_Rascal_Result_Value() 127 | { 128 | for (var iteration = 0; iteration < Iterations; iteration++) 129 | _ = RascalResultTValueOk.GetValueOrDefault(); 130 | } 131 | 132 | [Benchmark] 133 | [BenchmarkCategory("C03: Determining if a result is failed")] 134 | public void E_Rascal_Result_IsFailed() 135 | { 136 | for (var iteration = 0; iteration < Iterations; iteration++) 137 | _ = RascalResultFail.IsError; 138 | } 139 | 140 | [Benchmark] 141 | [BenchmarkCategory("C04: Determining if a result contains a specific error")] 142 | public void E_Rascal_Result_HasError() 143 | { 144 | for (var iteration = 0; iteration < Iterations; iteration++) 145 | _ = RascalResultFailWithErrorMessage.TryGetError(out var error) && error is StringError; 146 | } 147 | 148 | [Benchmark] 149 | [BenchmarkCategory("C05: Retrieving the first error")] 150 | public void E_Rascal_Result_FirstError() 151 | { 152 | for (var iteration = 0; iteration < Iterations; iteration++) 153 | _ = RascalResultFailWithErrorMessage.TryGetError(out _); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/Benchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Columns; 3 | using BenchmarkDotNet.Configs; 4 | using BenchmarkDotNet.Jobs; 5 | 6 | namespace LightResults.ComparisonBenchmarks; 7 | 8 | [MemoryDiagnoser] 9 | [SimpleJob(RuntimeMoniker.Net90)] 10 | [IterationTime(250)] 11 | [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] 12 | [CategoriesColumn] 13 | [HideColumns(Column.Job, Column.Iterations, Column.Error, Column.StdDev, Column.Median, Column.RatioSD, Column.Gen0, Column.Gen1, Column.Gen2)] 14 | public partial class Benchmarks 15 | { 16 | [Params(10)] 17 | public int Iterations { get; set; } 18 | 19 | private const int ResultValue = 0; 20 | private const string ErrorMessage = "An unknown error occurred."; 21 | } 22 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/LightResults.ComparisonBenchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | false 9 | false 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tools/LightResults.ComparisonBenchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Configs; 2 | using BenchmarkDotNet.Running; 3 | using LightResults.ComparisonBenchmarks; 4 | 5 | BenchmarkRunner.Run(ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator)); 6 | -------------------------------------------------------------------------------- /tools/LightResults.CurrentBenchmarks/Benchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Columns; 3 | using BenchmarkDotNet.Jobs; 4 | 5 | namespace LightResults.CurrentBenchmarks; 6 | 7 | // ReSharper disable RedundantTypeArgumentsOfMethod 8 | [MemoryDiagnoser] 9 | [SimpleJob(RuntimeMoniker.Net90)] 10 | [IterationTime(250)] 11 | [HideColumns(Column.Job, Column.Iterations, Column.Error, Column.StdDev, Column.Median, Column.RatioSD, Column.Gen0, Column.Gen1, Column.Gen2)] 12 | public class Benchmarks 13 | { 14 | [Params(10)] 15 | // ReSharper disable once UnusedAutoPropertyAccessor.Global 16 | public int Iterations { get; set; } 17 | 18 | private const int ResultValue = 0; 19 | private const string ErrorMessage = "An unknown error occurred."; 20 | private static readonly Error EmptyError = new(); 21 | private static readonly Error ErrorWithErrorMessage = new(ErrorMessage); 22 | private static readonly Result ResultSuccess = Result.Success(); 23 | private static readonly Result ResultFailure = Result.Failure(); 24 | private static readonly Result ResultFailureWithErrorMessage = Result.Failure(ErrorWithErrorMessage); 25 | private static readonly Result ResultTValueSuccess = Result.Success(ResultValue); 26 | private static readonly Result ResultTValueFailure = Result.Failure(); 27 | private static readonly Result ResultTValueFailureWithErrorMessage = Result.Failure(ErrorWithErrorMessage); 28 | 29 | [Benchmark] 30 | public void Current_Result_Ok() 31 | { 32 | for (var iteration = 0; iteration < Iterations; iteration++) 33 | _ = Result.Success(); 34 | } 35 | 36 | [Benchmark] 37 | public void Current_Result_Ok_ToString() 38 | { 39 | for (var iteration = 0; iteration < Iterations; iteration++) 40 | _ = ResultSuccess.ToString(); 41 | } 42 | 43 | [Benchmark] 44 | public void Current_Result_Fail() 45 | { 46 | for (var iteration = 0; iteration < Iterations; iteration++) 47 | _ = Result.Failure(); 48 | } 49 | 50 | [Benchmark] 51 | public void Current_Result_Fail_ToString() 52 | { 53 | for (var iteration = 0; iteration < Iterations; iteration++) 54 | _ = ResultFailure.ToString(); 55 | } 56 | 57 | [Benchmark] 58 | public void Current_Result_Fail_WithErrorMessage() 59 | { 60 | for (var iteration = 0; iteration < Iterations; iteration++) 61 | _ = Result.Failure(ErrorWithErrorMessage); 62 | } 63 | 64 | [Benchmark] 65 | public void Current_Result_Fail_WithErrorMessage_ToString() 66 | { 67 | for (var iteration = 0; iteration < Iterations; iteration++) 68 | _ = ResultFailureWithErrorMessage.ToString(); 69 | } 70 | 71 | [Benchmark] 72 | public void Current_Result_OkTValue() 73 | { 74 | for (var iteration = 0; iteration < Iterations; iteration++) 75 | _ = Result.Success(ResultValue); 76 | } 77 | 78 | [Benchmark] 79 | public void Current_Result_OkTValue_ToString() 80 | { 81 | for (var iteration = 0; iteration < Iterations; iteration++) 82 | _ = ResultTValueSuccess.ToString(); 83 | } 84 | 85 | [Benchmark] 86 | public void Current_Result_FailTValue() 87 | { 88 | for (var iteration = 0; iteration < Iterations; iteration++) 89 | _ = Result.Failure(); 90 | } 91 | 92 | [Benchmark] 93 | public void Current_Result_FailTValue_ToString() 94 | { 95 | for (var iteration = 0; iteration < Iterations; iteration++) 96 | _ = ResultTValueFailure.ToString(); 97 | } 98 | 99 | [Benchmark] 100 | public void Current_Result_FailTValue_WithErrorMessage() 101 | { 102 | for (var iteration = 0; iteration < Iterations; iteration++) 103 | _ = Result.Failure(ErrorWithErrorMessage); 104 | } 105 | 106 | [Benchmark] 107 | public void Current_Result_FailTValue_WithErrorMessage_ToString() 108 | { 109 | for (var iteration = 0; iteration < Iterations; iteration++) 110 | _ = ResultTValueFailureWithErrorMessage.ToString(); 111 | } 112 | 113 | [Benchmark] 114 | public void Current_Result_HasError() 115 | { 116 | for (var iteration = 0; iteration < Iterations; iteration++) 117 | _ = ResultFailureWithErrorMessage.HasError(); 118 | } 119 | 120 | [Benchmark] 121 | public void Current_Result_Error() 122 | { 123 | for (var iteration = 0; iteration < Iterations; iteration++) 124 | _ = ResultFailureWithErrorMessage.Errors.First(); 125 | } 126 | 127 | [Benchmark] 128 | public void Current_Error_New() 129 | { 130 | for (var iteration = 0; iteration < Iterations; iteration++) 131 | _ = new Error(); 132 | } 133 | 134 | [Benchmark] 135 | public void Current_Error_New_ToString() 136 | { 137 | for (var iteration = 0; iteration < Iterations; iteration++) 138 | _ = EmptyError.ToString(); 139 | } 140 | 141 | [Benchmark] 142 | public void Current_Error_New_WithErrorMessage() 143 | { 144 | for (var iteration = 0; iteration < Iterations; iteration++) 145 | _ = new Error(ErrorMessage); 146 | } 147 | 148 | [Benchmark] 149 | public void Current_Error_New_WithErrorMessage_ToString() 150 | { 151 | for (var iteration = 0; iteration < Iterations; iteration++) 152 | _ = ErrorWithErrorMessage.ToString(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /tools/LightResults.CurrentBenchmarks/LightResults.CurrentBenchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | false 9 | false 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tools/LightResults.CurrentBenchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using LightResults.CurrentBenchmarks; 3 | 4 | BenchmarkRunner.Run(); 5 | -------------------------------------------------------------------------------- /tools/LightResults.DevelopBenchmarks/Benchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Columns; 3 | using BenchmarkDotNet.Jobs; 4 | 5 | namespace LightResults.DevelopBenchmarks; 6 | 7 | // ReSharper disable RedundantTypeArgumentsOfMethod 8 | [MemoryDiagnoser] 9 | [SimpleJob(RuntimeMoniker.Net90)] 10 | [IterationTime(250)] 11 | [HideColumns(Column.Job, Column.Iterations, Column.Error, Column.StdDev, Column.Median, Column.RatioSD, Column.Gen0, Column.Gen1, Column.Gen2)] 12 | public class Benchmarks 13 | { 14 | [Params(10)] 15 | public int Iterations { get; set; } 16 | 17 | private const int ResultValue = 0; 18 | private const string ErrorMessage = "An unknown error occurred."; 19 | private static readonly Error EmptyError = new(); 20 | private static readonly Error ErrorWithErrorMessage = new(ErrorMessage); 21 | private static readonly Result ResultOk = Result.Success(); 22 | private static readonly Result ResultFail = Result.Failure(); 23 | private static readonly Result ResultFailWithErrorMessage = Result.Failure(ErrorWithErrorMessage); 24 | private static readonly Result ResultTValueOk = Result.Success(ResultValue); 25 | private static readonly Result ResultTValueFail = Result.Failure(); 26 | private static readonly Result ResultTValueFailWithErrorMessage = Result.Failure(ErrorWithErrorMessage); 27 | 28 | [Benchmark] 29 | public void Develop_Result_Ok() 30 | { 31 | for (var iteration = 0; iteration < Iterations; iteration++) 32 | _ = Result.Success(); 33 | } 34 | 35 | [Benchmark] 36 | public void Develop_Result_Ok_ToString() 37 | { 38 | for (var iteration = 0; iteration < Iterations; iteration++) 39 | _ = ResultOk.ToString(); 40 | } 41 | 42 | [Benchmark] 43 | public void Develop_Result_Fail() 44 | { 45 | for (var iteration = 0; iteration < Iterations; iteration++) 46 | _ = Result.Failure(); 47 | } 48 | 49 | [Benchmark] 50 | public void Develop_Result_Fail_ToString() 51 | { 52 | for (var iteration = 0; iteration < Iterations; iteration++) 53 | _ = ResultFail.ToString(); 54 | } 55 | 56 | [Benchmark] 57 | public void Develop_Result_Fail_WithErrorMessage() 58 | { 59 | for (var iteration = 0; iteration < Iterations; iteration++) 60 | _ = Result.Failure(ErrorWithErrorMessage); 61 | } 62 | 63 | [Benchmark] 64 | public void Develop_Result_Fail_WithErrorMessage_ToString() 65 | { 66 | for (var iteration = 0; iteration < Iterations; iteration++) 67 | _ = ResultFailWithErrorMessage.ToString(); 68 | } 69 | 70 | [Benchmark] 71 | public void Develop_Result_OkTValue() 72 | { 73 | for (var iteration = 0; iteration < Iterations; iteration++) 74 | _ = Result.Success(ResultValue); 75 | } 76 | 77 | [Benchmark] 78 | public void Develop_Result_OkTValue_ToString() 79 | { 80 | for (var iteration = 0; iteration < Iterations; iteration++) 81 | _ = ResultTValueOk.ToString(); 82 | } 83 | 84 | [Benchmark] 85 | public void Develop_Result_FailTValue() 86 | { 87 | for (var iteration = 0; iteration < Iterations; iteration++) 88 | _ = Result.Failure(); 89 | } 90 | 91 | [Benchmark] 92 | public void Develop_Result_FailTValue_ToString() 93 | { 94 | for (var iteration = 0; iteration < Iterations; iteration++) 95 | _ = ResultTValueFail.ToString(); 96 | } 97 | 98 | [Benchmark] 99 | public void Develop_Result_FailTValue_WithErrorMessage() 100 | { 101 | for (var iteration = 0; iteration < Iterations; iteration++) 102 | _ = Result.Failure(ErrorWithErrorMessage); 103 | } 104 | 105 | [Benchmark] 106 | public void Develop_Result_FailTValue_WithErrorMessage_ToString() 107 | { 108 | for (var iteration = 0; iteration < Iterations; iteration++) 109 | _ = ResultTValueFailWithErrorMessage.ToString(); 110 | } 111 | 112 | [Benchmark] 113 | public void Develop_Result_HasError() 114 | { 115 | for (var iteration = 0; iteration < Iterations; iteration++) 116 | _ = ResultFailWithErrorMessage.HasError(); 117 | } 118 | 119 | [Benchmark] 120 | public void Develop_Result_HasError_Out() 121 | { 122 | for (var iteration = 0; iteration < Iterations; iteration++) 123 | _ = ResultFailWithErrorMessage.HasError(out _); 124 | } 125 | 126 | [Benchmark] 127 | public void Develop_Result_Error() 128 | { 129 | for (var iteration = 0; iteration < Iterations; iteration++) 130 | _ = ResultFailWithErrorMessage.IsFailure(out _); 131 | } 132 | 133 | [Benchmark] 134 | public void Develop_Error_New() 135 | { 136 | for (var iteration = 0; iteration < Iterations; iteration++) 137 | _ = new Error(); 138 | } 139 | 140 | [Benchmark] 141 | public void Develop_Error_New_ToString() 142 | { 143 | for (var iteration = 0; iteration < Iterations; iteration++) 144 | _ = EmptyError.ToString(); 145 | } 146 | 147 | [Benchmark] 148 | public void Develop_Error_New_WithErrorMessage() 149 | { 150 | for (var iteration = 0; iteration < Iterations; iteration++) 151 | _ = new Error(ErrorMessage); 152 | } 153 | 154 | [Benchmark] 155 | public void Develop_Error_New_WithErrorMessage_ToString() 156 | { 157 | for (var iteration = 0; iteration < Iterations; iteration++) 158 | _ = ErrorWithErrorMessage.ToString(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /tools/LightResults.DevelopBenchmarks/LightResults.DevelopBenchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | false 9 | false 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tools/LightResults.DevelopBenchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using LightResults.DevelopBenchmarks; 3 | 4 | BenchmarkRunner.Run(); 5 | -------------------------------------------------------------------------------- /tools/LightResults.DevelopBenchmarks/Working.md: -------------------------------------------------------------------------------- 1 | BenchmarkDotNet v0.15.2, Windows 11 (10.0.26100.4652/24H2/2024Update/HudsonValley) 2 | 13th Gen Intel Core i7-13700KF 3.40GHz, 1 CPU, 24 logical and 16 physical cores 3 | .NET SDK 10.0.100-preview.6.25358.103 4 | [Host] : .NET 9.0.7 (9.0.725.31616), X64 RyuJIT AVX2 5 | .NET 9.0 : .NET 9.0.7 (9.0.725.31616), X64 RyuJIT AVX2 6 | 7 | Job=.NET 9.0 Runtime=.NET 9.0 IterationTime=250ms 8 | Iterations=10 9 | 10 | | Method | Categories | Mean | Ratio | Allocated | Alloc Ratio | 11 | |-------------------------------------------------------------|---------------------------------------------------------------------------|-------------:|-------:|----------:|------------:| 12 | | A_LightResults_Result_Ok | A01: Returning a successful result | 2.567 ns | 1.00 | - | NA | 13 | | B_FluentResults_Result_Ok | A01: Returning a successful result | 119.723 ns | 46.65 | 560 B | NA | 14 | | C_ArdalisResult_Result_Ok | A01: Returning a successful result | 277.651 ns | 108.18 | 3680 B | NA | 15 | | D_SimpleResults_Result_Ok | A01: Returning a successful result | 638.816 ns | 248.89 | 400 B | NA | 16 | | E_Rascal_Result_Ok | A01: Returning a successful result | 2.574 ns | 1.00 | - | NA | 17 | | | | | | | | 18 | | A_LightResults_Result_OkTValue | A02: Returning a successful value result | 2.661 ns | 1.00 | - | NA | 19 | | B_FluentResults_Result_OkTValue | A02: Returning a successful value result | 365.683 ns | 137.39 | 1200 B | NA | 20 | | C_ArdalisResult_Result_OkTValue | A02: Returning a successful value result | 278.231 ns | 104.56 | 3680 B | NA | 21 | | D_SimpleResults_Result_OkTValue | A02: Returning a successful value result | 650.526 ns | 244.46 | 480 B | NA | 22 | | E_Rascal_Result_OkTValue | A02: Returning a successful value result | 2.573 ns | 0.97 | - | NA | 23 | | | | | | | | 24 | | A_LightResults_Result_Fail | A03: Returning a failed result | 2.559 ns | 1.00 | - | NA | 25 | | B_FluentResults_Result_Fail | A03: Returning a failed result | 489.271 ns | 191.21 | 2640 B | NA | 26 | | C_ArdalisResult_Result_Fail | A03: Returning a failed result | 876.824 ns | 342.67 | 7040 B | NA | 27 | | D_SimpleResults_Result_Fail | A03: Returning a failed result | 665.515 ns | 260.09 | 400 B | NA | 28 | | E_Rascal_Result_Fail | A03: Returning a failed result | 20.893 ns | 8.16 | 240 B | NA | 29 | | | | | | | | 30 | | A_LightResults_Result_Fail_WithErrorMessage | A04: Returning a failed result with an error message | 25.014 ns | 1.00 | 320 B | 1.00 | 31 | | B_FluentResults_Result_Fail_WithErrorMessage | A04: Returning a failed result with an error message | 238.739 ns | 9.54 | 1120 B | 3.50 | 32 | | C_ArdalisResult_Result_Fail_WithErrorMessage | A04: Returning a failed result with an error message | 928.637 ns | 37.13 | 8000 B | 25.00 | 33 | | D_SimpleResults_Result_Fail_WithErrorMessage | A04: Returning a failed result with an error message | 338.846 ns | 13.55 | 400 B | 1.25 | 34 | | E_Rascal_Result_Fail_WithErrorMessage | A04: Returning a failed result with an error message | 2.557 ns | 0.10 | - | 0.00 | 35 | | | | | | | | 36 | | A_LightResults_Result_FailTValue | A05: Returning a failed value result | 2.597 ns | 1.00 | - | NA | 37 | | B_FluentResults_Result_FailTValue | A05: Returning a failed value result | 494.186 ns | 190.30 | 2720 B | NA | 38 | | C_ArdalisResult_Result_FailTValue | A05: Returning a failed value result | 279.461 ns | 107.62 | 3680 B | NA | 39 | | D_SimpleResults_Result_FailTValue | A05: Returning a failed value result | 995.774 ns | 383.41 | 880 B | NA | 40 | | E_Rascal_Result_FailTValue | A05: Returning a failed value result | 21.266 ns | 8.19 | 240 B | NA | 41 | | | | | | | | 42 | | A_LightResults_Result_FailTValue_WithErrorMessage | A06: Returning a failed value result with an error message | 22.537 ns | 1.00 | 320 B | 1.00 | 43 | | B_FluentResults_Result_FailTValue_WithErrorMessage | A06: Returning a failed value result with an error message | 239.709 ns | 10.64 | 1200 B | 3.75 | 44 | | C_ArdalisResult_Result_FailTValue_WithErrorMessage | A06: Returning a failed value result with an error message | 525.110 ns | 23.29 | 5680 B | 17.75 | 45 | | D_SimpleResults_Result_FailTValue_WithErrorMessage | A06: Returning a failed value result with an error message | 684.760 ns | 30.39 | 880 B | 2.75 | 46 | | E_Rascal_Result_FailTValue_WithErrorMessage | A06: Returning a failed value result with an error message | 2.551 ns | 0.11 | - | 0.00 | 47 | | | | | | | | 48 | | A_LightResults_Result_Ok_ToString | B01: String representation of a successful result | 2.713 ns | 1.00 | - | NA | 49 | | B_FluentResults_Result_Ok_ToString | B01: String representation of a successful result | 688.553 ns | 253.78 | 1520 B | NA | 50 | | C_ArdalisResult_Result_Ok_ToString | B01: String representation of a successful result | 18.577 ns | 6.85 | - | NA | 51 | | D_SimpleResults_Result_Ok_ToString | B01: String representation of a successful result | 18.284 ns | 6.74 | - | NA | 52 | | E_Rascal_Result_Ok_ToString | B01: String representation of a successful result | 219.600 ns | 80.94 | 480 B | NA | 53 | | | | | | | | 54 | | A_LightResults_Result_OkTValue_ToString | B02: String representation of a successful value result | 94.202 ns | 1.00 | 1040 B | 1.00 | 55 | | B_FluentResults_Result_OkTValue_ToString | B02: String representation of a successful value result | 949.433 ns | 10.08 | 3120 B | 3.00 | 56 | | C_ArdalisResult_Result_OkTValue_ToString | B02: String representation of a successful value result | 18.344 ns | 0.19 | - | 0.00 | 57 | | D_SimpleResults_Result_OkTValue_ToString | B02: String representation of a successful value result | 18.346 ns | 0.19 | - | 0.00 | 58 | | E_Rascal_Result_OkTValue_ToString | B02: String representation of a successful value result | 212.399 ns | 2.25 | 400 B | 0.38 | 59 | | | | | | | | 60 | | A_LightResults_Result_Fail_ToString | B03: String representation of a failed result | 5.481 ns | 1.00 | - | NA | 61 | | B_FluentResults_Result_Fail_ToString | B03: String representation of a failed result | 1,725.632 ns | 314.84 | 3920 B | NA | 62 | | C_ArdalisResult_Result_Fail_ToString | B03: String representation of a failed result | 18.440 ns | 3.36 | - | NA | 63 | | D_SimpleResults_Result_Fail_ToString | B03: String representation of a failed result | 18.643 ns | 3.40 | - | NA | 64 | | E_Rascal_Result_Fail_ToString | B03: String representation of a failed result | 228.379 ns | 41.68 | 480 B | NA | 65 | | | | | | | | 66 | | A_LightResults_Result_Fail_WithErrorMessage_ToString | B04: String representation of a failed result with an error message | 90.450 ns | 1.00 | 1520 B | 1.00 | 67 | | B_FluentResults_Result_Fail_WithErrorMessage_ToString | B04: String representation of a failed result with an error message | 2,275.817 ns | 25.16 | 9360 B | 6.16 | 68 | | C_ArdalisResult_Result_Fail_WithErrorMessage_ToString | B04: String representation of a failed result with an error message | 18.475 ns | 0.20 | - | 0.00 | 69 | | D_SimpleResults_Result_Fail_WithErrorMessage_ToString | B04: String representation of a failed result with an error message | 18.462 ns | 0.20 | - | 0.00 | 70 | | E_Rascal_Result_Fail_WithErrorMessage_ToString | B04: String representation of a failed result with an error message | 266.069 ns | 2.94 | 960 B | 0.63 | 71 | | | | | | | | 72 | | A_LightResults_Result_FailTValue_ToString | B05: String representation of a failed value result | 5.463 ns | 1.00 | - | NA | 73 | | B_FluentResults_Result_FailTValue_ToString | B05: String representation of a failed value result | 2,055.815 ns | 376.39 | 5840 B | NA | 74 | | C_ArdalisResult_Result_FailTValue_ToString | B05: String representation of a failed value result | 18.689 ns | 3.42 | - | NA | 75 | | D_SimpleResults_Result_FailTValue_ToString | B05: String representation of a failed value result | 18.772 ns | 3.44 | - | NA | 76 | | E_Rascal_Result_FailTValue_ToString | B05: String representation of a failed value result | 231.157 ns | 42.32 | 480 B | NA | 77 | | | | | | | | 78 | | A_LightResults_Result_FailTValue_WithErrorMessage_ToString | B06: String representation of a failed value result with an error message | 91.437 ns | 1.00 | 1520 B | 1.00 | 79 | | B_FluentResults_Result_FailTValue_WithErrorMessage_ToString | B06: String representation of a failed value result with an error message | 2,749.966 ns | 30.08 | 12160 B | 8.00 | 80 | | C_ArdalisResult_Result_FailTValue_WithErrorMessage_ToString | B06: String representation of a failed value result with an error message | 18.556 ns | 0.20 | - | 0.00 | 81 | | D_SimpleResults_Result_FailTValue_WithErrorMessage_ToString | B06: String representation of a failed value result with an error message | 18.287 ns | 0.20 | - | 0.00 | 82 | | E_Rascal_Result_FailTValue_WithErrorMessage_ToString | B06: String representation of a failed value result with an error message | 248.487 ns | 2.72 | 960 B | 0.63 | 83 | | | | | | | | 84 | | A_LightResults_Result_IsSuccess | C01: Determining if a result is successful | 2.568 ns | 1.00 | - | NA | 85 | | B_FluentResults_Result_IsSuccess | C01: Determining if a result is successful | 236.091 ns | 91.93 | 560 B | NA | 86 | | C_ArdalisResult_Result_IsSuccess | C01: Determining if a result is successful | 2.452 ns | 0.95 | - | NA | 87 | | D_SimpleResults_Result_IsSuccess | C01: Determining if a result is successful | 2.352 ns | 0.92 | - | NA | 88 | | E_Rascal_Result_IsSuccess | C01: Determining if a result is successful | 2.568 ns | 1.00 | - | NA | 89 | | | | | | | | 90 | | A_LightResults_Result_Value | C02: Retrieving the value | 2.442 ns | 1.00 | - | NA | 91 | | B_FluentResults_Result_Value | C02: Retrieving the value | 248.593 ns | 101.81 | 560 B | NA | 92 | | C_ArdalisResult_Result_Value | C02: Retrieving the value | 2.351 ns | 0.96 | - | NA | 93 | | D_SimpleResults_Result_Value | C02: Retrieving the value | 2.588 ns | 1.06 | - | NA | 94 | | E_Rascal_Result_Value | C02: Retrieving the value | 2.662 ns | 1.09 | - | NA | 95 | | | | | | | | 96 | | A_LightResults_Result_IsFailed | C03: Determining if a result is failed | 2.589 ns | 1.00 | - | NA | 97 | | B_FluentResults_Result_IsFailed | C03: Determining if a result is failed | 301.690 ns | 116.48 | 960 B | NA | 98 | | C_ArdalisResult_Result_IsFailed | C03: Determining if a result is failed | 2.541 ns | 0.98 | - | NA | 99 | | D_SimpleResults_Result_IsFailed | C03: Determining if a result is failed | 2.581 ns | 1.00 | - | NA | 100 | | E_Rascal_Result_IsFailed | C03: Determining if a result is failed | 2.607 ns | 1.01 | - | NA | 101 | | | | | | | | 102 | | A_LightResults_Result_HasError | C04: Determining if a result contains a specific error | 9.793 ns | 1.00 | - | NA | 103 | | B_FluentResults_Result_HasError | C04: Determining if a result contains a specific error | 1,307.727 ns | 133.54 | 4240 B | NA | 104 | | C_ArdalisResult_Result_HasError | C04: Determining if a result contains a specific error | 115.267 ns | 11.77 | 400 B | NA | 105 | | D_SimpleResults_Result_HasError | C04: Determining if a result contains a specific error | 2.603 ns | 0.27 | - | NA | 106 | | E_Rascal_Result_HasError | C04: Determining if a result contains a specific error | 10.530 ns | 1.08 | - | NA | 107 | | | | | | | | 108 | | A_LightResults_Result_FirstError | C05: Retrieving the first error | 2.599 ns | 1.00 | - | NA | 109 | | B_FluentResults_Result_FirstError | C05: Retrieving the first error | 490.054 ns | 188.58 | 1840 B | NA | 110 | | C_ArdalisResult_Result_FirstError | C05: Retrieving the first error | 99.470 ns | 38.28 | - | NA | 111 | | D_SimpleResults_Result_FirstError | C05: Retrieving the first error | 32.582 ns | 12.54 | - | NA | 112 | | E_Rascal_Result_FirstError | C05: Retrieving the first error | 11.795 ns | 4.54 | - | NA | 113 | 114 | | Method | Mean | Allocated | 115 | |-----------------------------------------------------|-----------:|----------:| 116 | | Current_Result_Ok | 2.719 ns | - | 117 | | Current_Result_Ok_ToString | 2.867 ns | - | 118 | | Current_Result_Fail | 2.569 ns | - | 119 | | Current_Result_Fail_ToString | 6.028 ns | - | 120 | | Current_Result_Fail_WithErrorMessage | 49.422 ns | 560 B | 121 | | Current_Result_Fail_WithErrorMessage_ToString | 287.388 ns | 2480 B | 122 | | Current_Result_OkTValue | 37.276 ns | 320 B | 123 | | Current_Result_OkTValue_ToString | 253.089 ns | 1520 B | 124 | | Current_Result_FailTValue | 2.440 ns | - | 125 | | Current_Result_FailTValue_ToString | 6.106 ns | - | 126 | | Current_Result_FailTValue_WithErrorMessage | 49.910 ns | 640 B | 127 | | Current_Result_FailTValue_WithErrorMessage_ToString | 251.820 ns | 2480 B | 128 | | Current_Result_HasError | 9.633 ns | - | 129 | | Current_Result_Error | 3.293 ns | - | 130 | | Current_Error_New | 31.986 ns | 320 B | 131 | | Current_Error_New_ToString | 36.593 ns | - | 132 | | Current_Error_New_WithErrorMessage | 31.509 ns | 320 B | 133 | | Current_Error_New_WithErrorMessage_ToString | 212.182 ns | 1200 B | 134 | 135 | | Method | Mean | Allocated | 136 | |-----------------------------------------------------|-----------:|----------:| 137 | | Develop_Result_Ok | 2.647 ns | - | 138 | | Develop_Result_Ok_ToString | 2.732 ns | - | 139 | | Develop_Result_Fail | 2.377 ns | - | 140 | | Develop_Result_Fail_ToString | 5.485 ns | - | 141 | | Develop_Result_Fail_WithErrorMessage | 25.435 ns | 320 B | 142 | | Develop_Result_Fail_WithErrorMessage_ToString | 90.408 ns | 1520 B | 143 | | Develop_Result_OkTValue | 2.489 ns | - | 144 | | Develop_Result_OkTValue_ToString | 91.808 ns | 1040 B | 145 | | Develop_Result_FailTValue | 2.500 ns | - | 146 | | Develop_Result_FailTValue_ToString | 5.450 ns | - | 147 | | Develop_Result_FailTValue_WithErrorMessage | 22.273 ns | 320 B | 148 | | Develop_Result_FailTValue_WithErrorMessage_ToString | 90.424 ns | 1520 B | 149 | | Develop_Result_HasError | 9.708 ns | - | 150 | | Develop_Result_Error | 2.616 ns | - | 151 | | Develop_Error_New | 31.918 ns | 320 B | 152 | | Develop_Error_New_ToString | 42.013 ns | - | 153 | | Develop_Error_New_WithErrorMessage | 32.300 ns | 320 B | 154 | | Develop_Error_New_WithErrorMessage_ToString | 140.613 ns | 1200 B | 155 | --------------------------------------------------------------------------------