REslava.ResultFlow β Library-agnostic Alternative
Feature Comparison
For projects not using REslava.Result, REslava.ResultFlow works with any fluent Result library (ErrorOr, FluentResults, LanguageExt, or your own):
dotnet add package REslava.ResultFlow
Add using REslava.ResultFlow; β the attribute is injected automatically by the generator.
Built-in convention dictionary β zero configuration for the most popular methods:
| Library | Recognized methods |
|---|---|
| REslava.Result | Ensure, Bind, Map, Tap, TapOnFailure, TapBoth, Match, WithSuccess (+ Async) |
| ErrorOr | Then, ThenAsync, Switch, SwitchAsync |
| LanguageExt | Filter, Do, DoAsync, DoLeft, DoLeftAsync |
| FluentResults | Bind, Map |
Any unrecognized method renders as a generic operation node.
What REslava.ResultFlow does NOT have (compared to REslava.Result.Flow)
For projects using REslava.Result, the companion package REslava.Result.Flow adds two capabilities not possible in the library-agnostic package:
- Typed error edges β scans each step's method body for
new XxxError(...)constructions whereXxxError implements IError; renders-->|DatabaseError| FAILedges instead of generic fail arrows - IOperation chain walker β uses Roslyn's semantic model at the
IInvocationOperationlevel, which makes the pipeline entry-point call (e.g.CreateUser()) visible as its own node
dotnet add package REslava.Result.Flow
Add using REslava.Result.Flow; β the attribute type is injected automatically by the generator, no separate assembly reference needed.
/*
```mermaid
flowchart LR
N0_CreateUser["CreateUser<br/>User"]:::operation
N0_CreateUser --> N1_EnsureAsync
N1_EnsureAsync["EnsureAsync β‘<br/>User"]:::gatekeeper
N1_EnsureAsync -->|pass| N2_BindAsync
N1_EnsureAsync -->|ValidationError| FAIL
N2_BindAsync["BindAsync β‘<br/>User"]:::transform
N2_BindAsync -->|ok| N3_MapAsync
N2_BindAsync -->|DatabaseError| FAIL
N3_MapAsync["MapAsync β‘<br/>User β UserDto"]:::transform
FAIL([fail]):::failure
classDef gatekeeper fill:#e3e9fa,color:#3f5c9a
classDef transform fill:#e3f0e8,color:#2f7a5c
classDef failure fill:#f8e3e3,color:#b13e3e
classDef operation fill:#e8f4f0,color:#1c7e6f
```*/
[ResultFlow]
public Task<Result<UserDto>> RegisterAsync(RegisterCommand cmd) =>
CreateUser(cmd)
.EnsureAsync(IsEmailValid, u => new ValidationError("email", "invalid"))
.BindAsync(SaveUser)
.MapAsync(ToUserDto);
Paste the comment into mermaid.live β CreateUser becomes the pipeline root, ValidationError and DatabaseError appear as typed failure edges on their respective steps.
Here you can see the resulting diagram:
Error scanning is best-effort β only scans methods in the same compilation; errors created but not returned may appear as false positives; helper methods are not recursively followed.
REslava.ResultFlow |
REslava.Result.Flow |
|
|---|---|---|
| Works with any library | β | β REslava.Result only |
| β‘ async annotation | β | β |
| Success type travel | β (first generic arg) | β (via IResultBase) |
| Typed error edges | β | β (IError body scan) |
| Entry-point call as node | β | β (IOperation walk) |