Rotomeca.Rop 1.0.0

Prefix Reserved
dotnet add package Rotomeca.Rop --version 1.0.0
                    
NuGet\Install-Package Rotomeca.Rop -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rotomeca.Rop" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Rotomeca.Rop" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Rotomeca.Rop" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Rotomeca.Rop --version 1.0.0
                    
#r "nuget: Rotomeca.Rop, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Rotomeca.Rop@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Rotomeca.Rop&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Rotomeca.Rop&version=1.0.0
                    
Install as a Cake Tool

Rotomeca.Rop

NuGet version CI License: ISC .NET Docs

Gestion d'erreurs typée à la Railway Oriented Programming pour C#. Inspiré du pattern Result<T, E> de Rust et des bonnes pratiques fonctionnelles, Rotomeca.Rop fournit une API cohérente, sûre et composable pour gérer les opérations pouvant échouer — sans exceptions intempestives, sans null silencieux.

Pensé pour s'aligner avec ses équivalents @rotomeca/rop (TypeScript) et rotomeca/rop (PHP), afin d'offrir des signatures et comportements quasi-identiques entre les trois langages.


Installation

dotnet add package Rotomeca.Rop

Compatibilité

Environnement Support
.NET Standard 2.1
.NET 8.0, 9.0
Source Link ✅ (navigation vers le code depuis le débogueur)
Symbols ✅ (.snupkg)
Nullable ✅ (activé)
Générateur ✅ ([Risky] / [Risky(Async = true)] + Roslyn)

Concepts clés

La librairie s'articule autour de quatre piliers :

Élément Rôle
IResult<TSuccess, TError> Résultat synchrone d'une opération — succès ou erreur, jamais les deux
IResultAsync<TSuccess, TError> Pendant asynchrone — pipeline sans await intermédiaire, directement awaitable
IError Erreur structurée : message, type sémantique, code applicatif, exception d'origine
[Risky] / [Risky(Async = true)] Génère automatiquement un wrapper Result.Try ou ResultAsync.TryAsync

Démarrage rapide

Créer un résultat synchrone

using Rotomeca.Rop;

// Succès
IResult<User> result = Result.Ok(new User(1, "Alice"));

// Erreur structurée
IResult<User> result = Result.Fail<User>(
    new Error("Utilisateur introuvable", ErrorType.NotFound, code: "USR_404"));

// Capture d'exception
IResult<int, IError> result = Result.Try(() => int.Parse(input));

Créer un résultat asynchrone

// Depuis une Task<T>
IResultAsync<User> result = ResultAsync.Ok(FetchUserFromDbAsync(id));

// Capture d'exception synchrone (task froide)
IResultAsync<User, IError> result = ResultAsync.Try(() => new Task<User>(...));

// Capture d'exception async complète (méthodes async)
Task<IResultAsync<User, IError>> result = ResultAsync.TryAsync(() => FetchUserAsync(id));

Composer un pipeline synchrone

var message = FindUser(userId)
    .AndThen(user => PlaceOrder(user, amount))
    .Map(order => order.Amount)
    .Match(
        Ok:  amount => $"Commande confirmée : {amount:C}",
        Err: error  => $"Échec ({error.Code}) : {error.Message}"
    );

Composer un pipeline asynchrone

// Chaînage sans await intermédiaire — un seul await à la fin
IResult<decimal> result = await FindUserAsync(userId)
    .AndThen(user => PlaceOrderAsync(user, amount))
    .MapAsync(order => EnrichOrderAsync(order))
    .Tap(order => logger.Info("Commande {id}", order.Id));

string message = result.Match(
    Ok:  amount => $"Confirmée : {amount:C}",
    Err: error  => $"Échec : {error.Message}"
);

Générer un wrapper [Risky]

public partial class UserService
{
    // Génère : public IResult<User, IError> Fetch(int id)
    [Risky]
    private User _Fetch(int id)
        => _repository.GetById(id) ?? throw new KeyNotFoundException();

    // Génère : public Task<IResultAsync<User, IError>> FetchAsync(int id)
    [Risky(Async = true)]
    private async Task<User> _FetchAsync(int id)
    {
        var user = await _repository.GetByIdAsync(id);
        return user ?? throw new KeyNotFoundException();
    }
}

Structure du dépôt

Rotomeca.Rop/
├── src/
│   ├── Rotomeca.Rop.Core/        # Librairie principale (IResult, IResultAsync, Error…)
│   └── Rotomeca.Rop.Generators/  # Générateur Roslyn ([Risky] sync et async)
└── tests/
    └── Rotomeca.Rop.Core.Tests/  # Suite de tests xUnit (sync + async)

ErrorType — catégories sémantiques

ErrorType est un record struct extensible. Les catégories prédéfinies couvrent les cas courants :

Valeur Usage
Unexpected Erreur inattendue ou non catégorisée (défaut)
Validation Données d'entrée invalides
NotFound Ressource introuvable
Unauthorized Accès non autorisé ou permissions manquantes
Conflict Conflit d'état sur la ressource
public static class AppErrors
{
    public static readonly ErrorType PaymentRequired = new("PaymentRequired");
    public static readonly ErrorType RateLimit       = new("RateLimit");
}

Référence des exports publics

// Factory synchrone
using Rotomeca.Rop;
Result.Ok<T>(value)           Result.Ok<T, E>(value)
Result.Fail<T>(error)         Result.Fail<T, E>(error)
Result.Fail(error)            Result.Try(fn)
Result.Throw(...)

// Factory asynchrone
ResultAsync.Ok<T>(task)       ResultAsync.Ok<T, E>(task)
ResultAsync.Fail<T>(task)     ResultAsync.Fail<T, E>(task)
ResultAsync.Try(fn)           ResultAsync.TryAsync(fn)
ResultAsync.Throw(...)

// Interfaces synchrones
using Rotomeca.Rop.Interfaces;
IResult<TSuccess, TError>     IResult<TSuccess>     IEmptyResult

// Interfaces asynchrones
IResultAsync<TSuccess, TError>     IResultAsync<TSuccess>     IEmptyResultAsync

// Modèle d'erreur
IError     Error     Error<TData>     ErrorType

// Extensions
using Rotomeca.Rop.Extensions;
.AsResult()    .WithError(mapper)    .As(value)    .As(factory)

// Générateur
[Risky]                  // wrapper Result.Try (sync)
[Risky(Async = true)]    // wrapper ResultAsync.TryAsync (async)

Ecosystème Rotomeca

Package Langage Description
Rotomeca.Rop C# Ce package
@rotomeca/rop TypeScript Gestion d'erreurs typée (Result<T, E>)
@rotomeca/event TypeScript Système d'événements typés à la C#
@rotomeca/utils TypeScript Fonctions pures, types brandés et helpers
@rotomeca/jsenumerable TypeScript LINQ lazy en TypeScript

Contribuer

git clone https://github.com/Rotomeca/rop.git
cd rop
dotnet restore
dotnet test

Les contributions sont les bienvenues via Pull Request sur la branche dev.


Note sur l'utilisation de l'IA

L'intégralité du code de ce projet a d'abord été écrite à la main en essayant d'avoir le C# le plus propre possible. L'IA a ensuite été utilisée pour :

  • Proposer des axes d'amélioration et de refactorisation si besoin, après relecture de ses modifications par mes soins
  • La documentation et les README — j'ai toujours été une bille en documentation, je trouve celle de l'IA lisible et explicite ; elle a toujours été relue et validée par mes soins
  • Les tests unitaires — tester, c'est facile, mais présenter des tests unitaires, c'est complexe (de mon point de vue) ; l'IA a dans un premier temps généré les tests, je les ai parcourus pour les comprendre et les corriger au besoin
  • La CI/CD — vu que ce n'est pas mon domaine, mais ça permet d'apprendre beaucoup 👍

Sa principale contribution a donc été de m'accompagner sur les points qui me sont lacunaires.


Licence

ISC © Rotomeca

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 51 5/31/2026