diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 4afa6ab..ab223bd 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -51,7 +51,7 @@ jobs:
shell: powershell
run: |
dotnet tool install --global dotnet-coverage
- .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_astar-dev-functional-extensions" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
+ .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_astar-dev-functional-extensions" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.scanner.scanAll=false
dotnet build
dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Tests.EndToEnd"' -f xml -o 'coverage.xml'
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
diff --git a/samples/AStar.Dev.ConsoleSample/AStar.Dev.ConsoleSample.csproj b/samples/AStar.Dev.ConsoleSample/AStar.Dev.ConsoleSample.csproj
index 6eedf0d..bf41120 100644
--- a/samples/AStar.Dev.ConsoleSample/AStar.Dev.ConsoleSample.csproj
+++ b/samples/AStar.Dev.ConsoleSample/AStar.Dev.ConsoleSample.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/samples/AStar.Dev.SampleApi/AStar.Dev.SampleApi.csproj b/samples/AStar.Dev.SampleApi/AStar.Dev.SampleApi.csproj
index bef8201..f3c3654 100644
--- a/samples/AStar.Dev.SampleApi/AStar.Dev.SampleApi.csproj
+++ b/samples/AStar.Dev.SampleApi/AStar.Dev.SampleApi.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/samples/AStar.Dev.SampleBlazor/AStar.Dev.SampleBlazor.csproj b/samples/AStar.Dev.SampleBlazor/AStar.Dev.SampleBlazor.csproj
index ad52d32..54627c2 100644
--- a/samples/AStar.Dev.SampleBlazor/AStar.Dev.SampleBlazor.csproj
+++ b/samples/AStar.Dev.SampleBlazor/AStar.Dev.SampleBlazor.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj b/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj
index 354cf8d..8b92d90 100644
--- a/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj
+++ b/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj
@@ -9,7 +9,7 @@
true
snupkg
AStar.Dev.Functional.Extensions
- 0.2.1-alpha
+ 0.2.2-alpha
Readme.md
Jason
AStar Development
@@ -25,6 +25,7 @@
true
AStar.Dev.Functional.Extensions
AStar Development 2025
+ No changes in this version, just extending the documentation
@@ -33,6 +34,8 @@
+
+
diff --git a/src/AStar.Dev.Functional.Extensions/Option{T}.cs b/src/AStar.Dev.Functional.Extensions/Option{T}.cs
index 783321b..652a18b 100644
--- a/src/AStar.Dev.Functional.Extensions/Option{T}.cs
+++ b/src/AStar.Dev.Functional.Extensions/Option{T}.cs
@@ -81,7 +81,7 @@ public Some(T value)
public sealed class None : Option
{
///
- /// A helper method to create an instance of
+ /// A helper method to create an instance of
///
public static readonly None Instance = new ();
diff --git a/src/AStar.Dev.Functional.Extensions/Readme-option.md b/src/AStar.Dev.Functional.Extensions/Readme-option.md
new file mode 100644
index 0000000..7c2b434
--- /dev/null
+++ b/src/AStar.Dev.Functional.Extensions/Readme-option.md
@@ -0,0 +1,103 @@
+# π― Option Functional Cheat Sheet
+
+## π§© Option Overview
+
+Represents a value that might exist (`Some`) or not (`None`), avoiding nulls and enabling functional composition.
+
+```csharp
+Option maybeNumber = Option.Some(42);
+Option emptyName = Option.None();
+```
+
+---
+
+## π Construction
+
+| Syntax | Description |
+|-----------------------------|-----------------------------------------|
+| `Option.Some(value)` | Wraps a non-null value as `Some` |
+| `Option.None()` | Creates a `None` of type `T` |
+| `value.ToOption()` | Converts value or default to Option |
+| `value.ToOption(predicate)` | Converts only if predicate returns true |
+| `nullable.ToOption()` | Converts nullable struct to Option |
+
+---
+
+## π§ͺ Pattern Matching
+
+```csharp
+option.Match(
+ some => $"Value: {some}",
+ () => "No value"
+);
+```
+
+Or via deconstruction:
+
+```csharp
+var (isSome, value) = option;
+```
+
+Or with TryGet:
+
+```csharp
+if (option.TryGetValue(out var value)) { /* use value */ }
+```
+
+---
+
+## π§ Transformation
+
+| Method | Description |
+|---------------------|---------------------------------------------|
+| `Map(func)` | Transforms value inside Some |
+| `Bind(func)` | Chains function that returns another Option |
+| `ToResult(errorFn)` | Converts Option to `Result` |
+| `ToNullable()` | Converts to nullable (structs only) |
+| `ToEnumerable()` | Converts to `IEnumerable` |
+
+---
+
+## πͺ LINQ Support
+
+```csharp
+var result =
+ from name in Option.Some("Jason")
+ from greeting in Option.Some($"Hello, {name}")
+ select greeting;
+```
+
+Via `Select`, `SelectMany`, or `SelectAwait` (async LINQ)
+
+---
+
+## π Async Support
+
+| Method | Description |
+|------------------------------|-----------------------------------------------|
+| `MapAsync(func)` | Awaits and maps value |
+| `BindAsync(func)` | Awaits and chains async Option-returning func |
+| `MatchAsync(onSome, onNone)` | Async pattern match |
+| `SelectAwait(func)` | LINQ-friendly async projection |
+
+---
+
+## π§― Fallbacks and Conversions
+
+```csharp
+option.OrElse("fallback"); // returns value or fallback
+option.OrThrow(); // throws if None
+option.IsSome(); // true if Some
+option.IsNone(); // true if None
+```
+
+---
+
+## π Debugging & Output
+
+```csharp
+option.ToString(); // Outputs "Some(value)" or "None"
+```
+
+---
+
diff --git a/src/AStar.Dev.Functional.Extensions/Readme-result.md b/src/AStar.Dev.Functional.Extensions/Readme-result.md
new file mode 100644
index 0000000..5c211ca
--- /dev/null
+++ b/src/AStar.Dev.Functional.Extensions/Readme-result.md
@@ -0,0 +1,96 @@
+# π§ Result Cheat Sheet
+
+## π§± Core Concept
+
+Result encapsulates either:
+
+β
Ok(T) β a success value
+
+β Error(TError) β an error reason
+
+```csharp
+Result nameResult = new Result.Ok("Jason");
+Result errorResult = new Result.Error("Invalid input");
+```
+
+## π Construction Helpers
+
+| Expression | Outcome |
+|--------------------------------|-----------------------------|
+| new Result.Ok(value) | Constructs a success result |
+| new Result.Error(reason) | Constructs an error result |
+
+## π§ Transformation
+
+| Method | Description |
+|-------------|--------------------------------------------------|
+| Map(fn) | Transforms the success value |
+| Bind(fn) | Chains to another result-returning function |
+| Tap(action) | Invokes side effect on success, returns original |
+
+```csharp
+result.Map(value => value.ToUpper());
+result.Bind(value => Validate(value));
+result.Tap(Console.WriteLine);
+```
+
+## π§ͺ Pattern Matching
+
+```csharp
+result.Match(
+onSuccess: value => $"β
{value}",
+onError: reason => $"β {reason}"
+);
+```
+
+## π§ LINQ Composition
+
+```csharp
+var final =
+from input in GetInput()
+from valid in Validate(input)
+select $"Welcome, {valid}";
+```
+
+## LINQ Methods
+
+| Method | Description |
+|----------------------|---------------------------------------------|
+| Select(fn) | Maps over success value |
+| SelectMany(fn) | Binds to next result |
+| SelectMany(..., ...) | Binds and projects from intermediate result |
+
+## β‘ Async Support
+
+```csharp
+var asyncResult = await resultTask.MapAsync(val => val.Length);
+var finalValue = await resultTask.MatchAsync(...);
+```
+
+## Async LINQ
+
+```csharp
+var result =
+await GetAsync()
+.SelectMany(asyncValue => ValidateAsync(asyncValue), (a, b) => $"{a}-{b}");
+```
+
+## π§― Error Handling
+
+```csharp
+if (result is Result.Error err)
+Log(err.Reason);
+```
+
+Or selectively tap into errors:
+
+```csharp
+public static Result TapError(
+this Result result,
+Action handler)
+{
+if (result is Result.Error error)
+handler(error.Reason);
+return result;
+}
+```
diff --git a/src/AStar.Dev.Functional.Extensions/Readme.md b/src/AStar.Dev.Functional.Extensions/Readme.md
index 7c2b434..dbf2f9d 100644
--- a/src/AStar.Dev.Functional.Extensions/Readme.md
+++ b/src/AStar.Dev.Functional.Extensions/Readme.md
@@ -1,103 +1,29 @@
-# π― Option Functional Cheat Sheet
+# AStar Dev Functional Extensions
-## π§© Option Overview
+## Overview
-Represents a value that might exist (`Some`) or not (`None`), avoiding nulls and enabling functional composition.
+This project contains a bunch of classes and extension methods to facilitate a more functional approach to handling errors and optional objects.
-```csharp
-Option maybeNumber = Option.Some(42);
-Option emptyName = Option.None();
-```
+## Cheat Sheets
----
+### Result<T> and associated extensions
-## π Construction
+Cheat sheet is [here](Readme-result.md)
-| Syntax | Description |
-|-----------------------------|-----------------------------------------|
-| `Option.Some(value)` | Wraps a non-null value as `Some` |
-| `Option.None()` | Creates a `None` of type `T` |
-| `value.ToOption()` | Converts value or default to Option |
-| `value.ToOption(predicate)` | Converts only if predicate returns true |
-| `nullable.ToOption()` | Converts nullable struct to Option |
+### Option<T> and associated extensions
----
+Cheat sheet is [here](Readme-option.md)
-## π§ͺ Pattern Matching
+## Build and analysis
-```csharp
-option.Match(
- some => $"Value: {some}",
- () => "No value"
-);
-```
+### GitHub build
-Or via deconstruction:
+[](https://github.com/astar-development/astar-dev-functional-extensions/actions/workflows/dotnet.yml)
-```csharp
-var (isSome, value) = option;
-```
+### SonarQube details
-Or with TryGet:
-
-```csharp
-if (option.TryGetValue(out var value)) { /* use value */ }
-```
-
----
-
-## π§ Transformation
-
-| Method | Description |
-|---------------------|---------------------------------------------|
-| `Map(func)` | Transforms value inside Some |
-| `Bind(func)` | Chains function that returns another Option |
-| `ToResult(errorFn)` | Converts Option to `Result` |
-| `ToNullable()` | Converts to nullable (structs only) |
-| `ToEnumerable()` | Converts to `IEnumerable` |
-
----
-
-## πͺ LINQ Support
-
-```csharp
-var result =
- from name in Option.Some("Jason")
- from greeting in Option.Some($"Hello, {name}")
- select greeting;
-```
-
-Via `Select`, `SelectMany`, or `SelectAwait` (async LINQ)
-
----
-
-## π Async Support
-
-| Method | Description |
-|------------------------------|-----------------------------------------------|
-| `MapAsync(func)` | Awaits and maps value |
-| `BindAsync(func)` | Awaits and chains async Option-returning func |
-| `MatchAsync(onSome, onNone)` | Async pattern match |
-| `SelectAwait(func)` | LINQ-friendly async projection |
-
----
-
-## π§― Fallbacks and Conversions
-
-```csharp
-option.OrElse("fallback"); // returns value or fallback
-option.OrThrow(); // throws if None
-option.IsSome(); // true if Some
-option.IsNone(); // true if None
-```
-
----
-
-## π Debugging & Output
-
-```csharp
-option.ToString(); // Outputs "Some(value)" or "None"
-```
-
----
+| | | | | |
+|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) |
+| [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) | [](https://sonarcloud.io/summary/new_code?id=astar-development_astar-dev-functional-extensions) |
diff --git a/src/AStar.Dev.Functional.Extensions/ResultAsyncExtensions.cs b/src/AStar.Dev.Functional.Extensions/ResultAsyncExtensions.cs
index 4155633..7737eb5 100644
--- a/src/AStar.Dev.Functional.Extensions/ResultAsyncExtensions.cs
+++ b/src/AStar.Dev.Functional.Extensions/ResultAsyncExtensions.cs
@@ -5,18 +5,22 @@
namespace AStar.Dev.Functional.Extensions;
///
+/// Provides asynchronous functional operations for wrapped in .
///
public static class ResultAsyncExtensions
{
///
+ /// Asynchronously transforms the success value of a if present.
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
+ /// The type of the original success value.
+ /// The type of the error value.
+ /// The type of the transformed success value.
+ /// A that wraps the result to transform.
+ /// A mapping function applied to the success value.
+ /// An optional cancellation token to cancel the operation.
+ ///
+ /// A containing either a transformed Ok result or the original Error.
+ ///
public static async ValueTask> MapAsync(
this ValueTask> task,
Func map,
@@ -29,14 +33,17 @@ public static async ValueTask> MapAsync(
}
///
+ /// Asynchronously chains a function that returns another if the current result is successful.
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
+ /// The type of the original success value.
+ /// The type of the error value.
+ /// The type of the success value returned by the binding function.
+ /// A containing the result to bind.
+ /// A function that returns a new asynchronous result.
+ /// An optional cancellation token to cancel the operation.
+ ///
+ /// A containing the bound result if successful, or the original Error.
+ ///
public static async ValueTask> BindAsync(
this ValueTask> task,
Func>> bind,
@@ -51,15 +58,18 @@ public static async ValueTask> BindAsync(
}
///
+ /// Asynchronously matches on a , executing the appropriate function depending on success or error.
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
+ /// The type of the success value.
+ /// The type of the error value.
+ /// The return type of the match operation.
+ /// A to evaluate.
+ /// A function invoked if the result is Ok.
+ /// A function invoked if the result is Error.
+ /// An optional cancellation token to cancel the operation.
+ ///
+ /// A containing the return value of the appropriate match function.
+ ///
public static async ValueTask MatchAsync(
this ValueTask> task,
Func onSuccess,
diff --git a/src/AStar.Dev.Functional.Extensions/ResultAsyncLinqExtensions.cs b/src/AStar.Dev.Functional.Extensions/ResultAsyncLinqExtensions.cs
index d5f44f8..eb18ea0 100644
--- a/src/AStar.Dev.Functional.Extensions/ResultAsyncLinqExtensions.cs
+++ b/src/AStar.Dev.Functional.Extensions/ResultAsyncLinqExtensions.cs
@@ -5,20 +5,32 @@
namespace AStar.Dev.Functional.Extensions;
///
+/// Provides LINQ-style asynchronous binding for wrapped in .
///
public static class ResultAsyncLinqExtensions
{
///
+ /// Asynchronously binds and projects over a using LINQ-style composition.
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
+ /// The type of the original success value.
+ /// The type of the error value.
+ /// The type of the intermediate value returned by the binding function.
+ /// The final projected result type.
+ ///
+ /// An asynchronous to be bound and projected.
+ ///
+ ///
+ /// A function that returns an asynchronous from the source value.
+ ///
+ ///
+ /// A projection function that combines the source and collection values into a final result.
+ ///
+ ///
+ /// A token used to cancel the operation before completion.
+ ///
+ ///
+ /// A containing either a projected Ok value or an Error from any failure.
+ ///
public static async ValueTask> SelectMany(
this ValueTask> source,
Func>> bind,
diff --git a/src/AStar.Dev.Functional.Extensions/ResultExtensions.cs b/src/AStar.Dev.Functional.Extensions/ResultExtensions.cs
index b6bfc10..5c335ac 100644
--- a/src/AStar.Dev.Functional.Extensions/ResultExtensions.cs
+++ b/src/AStar.Dev.Functional.Extensions/ResultExtensions.cs
@@ -3,17 +3,22 @@
namespace AStar.Dev.Functional.Extensions;
///
+/// Provides functional operations for transforming and composing .
///
public static class ResultExtensions
{
///
+ /// Transforms the success value of a using the specified mapping function.
///
- ///
- ///
- ///
- ///
- ///
- ///
+ /// The original type of the success value.
+ /// The type of the error value.
+ /// The type of the transformed success value.
+ /// The result to transform.
+ /// A function that maps the original value to a new value.
+ ///
+ /// A new containing the mapped success value if present,
+ /// or the original error if unsuccessful.
+ ///
public static Result Map(
this Result result,
Func map)
@@ -25,13 +30,17 @@ public static Result Map(
}
///
+ /// Chains the current result to another -producing function,
+ /// allowing for functional composition across result types.
///
- ///
- ///
- ///
- ///
- ///
- ///
+ /// The original type of the success value.
+ /// The type of the error value.
+ /// The type of the new result's success value.
+ /// The result to bind.
+ /// A function that returns a new .
+ ///
+ /// The result of the binding function if the original was successful; otherwise, the original error.
+ ///
public static Result Bind(
this Result result,
Func> bind)
@@ -43,12 +52,16 @@ public static Result Bind(
}
///
+ /// Executes a side-effect action on the success value of a ,
+ /// returning the original result unchanged.
///
- ///
- ///
- ///
- ///
- ///
+ /// The type of the success value.
+ /// The type of the error value.
+ /// The result to inspect.
+ /// An action to invoke if the result is successful.
+ ///
+ /// The original instance, unchanged.
+ ///
public static Result Tap(
this Result result,
Action action)
diff --git a/src/AStar.Dev.Functional.Extensions/ResultLinqExtensions.cs b/src/AStar.Dev.Functional.Extensions/ResultLinqExtensions.cs
index 9ebc779..ba3c5e4 100644
--- a/src/AStar.Dev.Functional.Extensions/ResultLinqExtensions.cs
+++ b/src/AStar.Dev.Functional.Extensions/ResultLinqExtensions.cs
@@ -3,21 +3,28 @@
namespace AStar.Dev.Functional.Extensions;
///
+/// Enables LINQ-style composition for types using Select and SelectMany.
///
public static class ResultLinqExtensions
{
///
+ /// Binds and projects over a using LINQ-style composition.
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static Result SelectMany(this Result source, Func> bind,
- Func project)
+ /// The type of the original success value.
+ /// The type of the error value.
+ /// The intermediate value returned by the binding function.
+ /// The final projected result value.
+ /// The initial result to bind.
+ /// A function that returns a result from the source value.
+ /// A projection that combines the source and bound values.
+ ///
+ /// A containing the projected success value,
+ /// or the first encountered error.
+ ///
+ public static Result SelectMany(
+ this Result source,
+ Func> bind,
+ Func project)
{
return source.Match(
ok => bind(ok).Match>(
@@ -29,27 +36,37 @@ public static Result SelectMany
+ /// Binds a result-producing function to a , enabling LINQ-style monadic chaining.
///
- ///
- ///
- ///
- ///
- ///
- ///
- public static Result SelectMany(this Result result, Func> binder)
+ /// The original type of the success value.
+ /// The type of the error value.
+ /// The type of the bound resultβs success value.
+ /// The original result to bind.
+ /// A function returning a new result from the success value.
+ ///
+ /// The bound result if successful, or the original error.
+ ///
+ public static Result SelectMany(
+ this Result result,
+ Func> binder)
{
return result.Bind(binder);
}
///
+ /// Transforms the success value of a using a mapping function.
///
- ///
- ///
- ///
- ///
- ///
- ///
- public static Result Select(this Result result, Func selector)
+ /// The original type of the success value.
+ /// The type of the error value.
+ /// The type of the transformed value.
+ /// The result to map.
+ /// A function that transforms the success value.
+ ///
+ /// A result containing the mapped value if successful, or the original error.
+ ///
+ public static Result Select(
+ this Result result,
+ Func selector)
{
return result.Map(selector);
}
diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj b/test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj
index 4ce6b8a..0a950d3 100644
--- a/test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj
+++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj
@@ -29,7 +29,7 @@
-
+