{"id":28716328,"url":"https://github.com/safwa1/sharpresults","last_synced_at":"2025-06-15T03:01:54.252Z","repository":{"id":292923136,"uuid":"982416892","full_name":"safwa1/SharpResults","owner":"safwa1","description":"A lightweight, zero-dependency C# library that implements the Result and Option types for more explicit and type-safe error handling. SharpResults helps you avoid exceptions for control flow and makes success/failure and presence/absence states explicit in your code.","archived":false,"fork":false,"pushed_at":"2025-06-09T19:20:50.000Z","size":45,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-09T20:26:57.946Z","etag":null,"topics":["csharp","dotnet","error-handling","extension-methods","fluent-api","functional-programming","library","monads","nuget","optianal","result-type","sharpresults"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/SharpResults","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/safwa1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-12T21:14:17.000Z","updated_at":"2025-06-09T19:20:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"db6d7db9-c58a-493a-a4ca-ffd5c0043737","html_url":"https://github.com/safwa1/SharpResults","commit_stats":null,"previous_names":["safwa1/sharpresults"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/safwa1/SharpResults","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwa1%2FSharpResults","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwa1%2FSharpResults/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwa1%2FSharpResults/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwa1%2FSharpResults/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/safwa1","download_url":"https://codeload.github.com/safwa1/SharpResults/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/safwa1%2FSharpResults/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259914865,"owners_count":22931323,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","error-handling","extension-methods","fluent-api","functional-programming","library","monads","nuget","optianal","result-type","sharpresults"],"created_at":"2025-06-15T03:00:26.765Z","updated_at":"2025-06-15T03:01:54.178Z","avatar_url":"https://github.com/safwa1.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SharpResults\n\nA lightweight, zero-dependency C# library that implements the Result and Option types for more explicit and type-safe error handling. SharpResults helps you avoid exceptions for control flow and makes success/failure and presence/absence states explicit in your code.\n\n\u003cdiv align=\"center\"\u003e\n\n![SharpResults Logo](https://img.shields.io/badge/SharpResults-Type--Safe%20Error%20Handling-blue)\n[![NuGet](https://img.shields.io/nuget/v/SharpResults.svg)](https://www.nuget.org/packages/SharpResults/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Downloads](https://img.shields.io/nuget/dt/SharpResults.svg)](https://www.nuget.org/packages/SharpResults/)\n\n\u003c/div\u003e\n\n## 🚀 Features\n\n- 🛡️ **Type-safe error handling** - No more unexpected exceptions\n- 🔄 **Chainable operations** - Fluent API for transforming and combining results\n- 🧩 **LINQ integration** - Works with C# query syntax\n- ⚡ **Async support** - First-class support for async/await operations\n- 🔍 **Comprehensive API** - Rich set of methods for working with results\n- 🪶 **Lightweight** - Zero dependencies\n- 🎯 **Multi-targeting** - Supports .NET 8.0 and .NET 9.0\n- 📊 **Performance optimized** - Minimal overhead compared to traditional exception handling\n\n## 📋 Table of Contents\n\n- [Installation](#-installation)\n- [Basic Usage](#basic-usage)\n- [Advanced Usage](#advanced-usage)\n- [Asynchronous Operations](#asynchronous-operations)\n- [Real-World Examples](#real-world-examples)\n- [Performance Considerations](#performance-considerations)\n- [Comparison with Other Libraries](#comparison-with-other-libraries)\n- [Contributing](#contributing)\n- [License](#license)\n\n## 📦 Installation\n\n### Package Manager\n\n```\nInstall-Package SharpResults\n```\n\n### .NET CLI\n\n```\ndotnet add package SharpResults\n```\n\n## Basic Usage\n\n### Creating Results\n\n```csharp\nusing SharpResults;\nusing SharpResults.Types;\n\n// Create a successful result\nResult\u003cint\u003e success = Result.Ok(42);\n\n// Create a failed result with an exception\nResult\u003cint\u003e failure = Result.Err\u003cint\u003e(new Exception(\"Something went wrong\"));\n\n// Create a failed result with just a message\nResult\u003cstring\u003e failureWithMessage = Result.Err\u003cstring\u003e(\"Invalid input\");\n\n// Implicit conversion from value to successful result\nResult\u003cint\u003e implicitSuccess = 42;\n```\n\n### Checking Result State\n\n```csharp\nResult\u003cint\u003e result = GetSomeResult();\n\n// Using properties\nif (result.IsOk)\n{\n    // Use result.Value safely here\n    Console.WriteLine($\"Got value: {result.Value}\");\n}\nelse\n{\n    // Handle the error\n    Console.WriteLine($\"Error: {result.Exception.Message}\");\n}\n\n// Using pattern matching (C# 8.0+)\nswitch (result)\n{\n    case { IsOk: true } ok:\n        Console.WriteLine($\"Success: {ok.Value}\");\n        break;\n    case { IsErr: true } err:\n        Console.WriteLine($\"Error: {err.Exception.Message}\");\n        break;\n}\n\n// Using deconstruction syntax\nvar (isOk, value, exception) = result;\nif (isOk)\n{\n    Console.WriteLine($\"Value: {value}\");\n}\n```\n\n### Transforming Results\n\n```csharp\n// Map transforms the value inside a successful result\nResult\u003cint\u003e result = Result.Ok(42);\nResult\u003cstring\u003e mapped = result.Map(x =\u003e x.ToString());\n\n// AndThen (or FlatMap) chains result-returning operations\nResult\u003cint\u003e parsed = Result.Ok(\"42\")\n    .AndThen(str =\u003e {\n        if (int.TryParse(str, out int value))\n            return Result.Ok(value);\n        return Result.Err\u003cint\u003e(\"Parsing failed\");\n    });\n\n// LINQ syntax support\nResult\u003cint\u003e computation = \n    from x in Result.Ok(10)\n    from y in Result.Ok(5)\n    select x + y; // Result.Ok(15)\n```\n\n### Handling Errors\n\n```csharp\n// Recover from errors\nResult\u003cint\u003e recovered = Result.Err\u003cint\u003e(\"Original error\")\n    .OrElse(ex =\u003e Result.Ok(42));\n\n// Provide default values\nint value = Result.Err\u003cint\u003e(\"Error\")\n    .UnwrapOr(42); // 42\n\n// Use a fallback function\nint computed = Result.Err\u003cint\u003e(new Exception(\"Failed\"))\n    .UnwrapOrElse(ex =\u003e 42); // 42\n\n// Map errors to different error types\nResult\u003cint, CustomError\u003e mappedError = Result.Err\u003cint\u003e(\"Database error\")\n    .MapErr(msg =\u003e new CustomError(ErrorType.Database, msg));\n```\n\n### Pattern Matching with Match\n\n```csharp\n// Transform both success and error cases\nstring message = result.Match(\n    ok: value =\u003e $\"Success: {value}\",\n    err: ex =\u003e $\"Error: {ex.Message}\"\n);\n\n// Execute actions based on result state\nresult.Match(\n    ok: value =\u003e SaveToDatabase(value),\n    err: ex =\u003e LogError(ex)\n);\n```\n\n## Advanced Usage\n\n### Custom Error Types\n\nUse `Result\u003cT, TError\u003e` when you want to use custom error types instead of exceptions:\n\n```csharp\n// Define a custom error type\npublic enum ApiError\n{\n    NotFound,\n    Unauthorized,\n    ServerError\n}\n\n// Create results with custom error types\nResult\u003cUser, ApiError\u003e GetUser(int id)\n{\n    if (id \u003c= 0)\n        return ApiError.NotFound; // Implicit conversion\n    \n    if (!IsAuthenticated())\n        return Result.Err\u003cUser, ApiError\u003e(ApiError.Unauthorized);\n    \n    try\n    {\n        var user = _repository.GetUser(id);\n        return user != null \n            ? Result.Ok\u003cUser, ApiError\u003e(user)\n            : ApiError.NotFound;\n    }\n    catch\n    {\n        return ApiError.ServerError;\n    }\n}\n\n// Usage\nvar result = GetUser(42);\n\n// Pattern matching with custom errors\nstring message = result.Match(\n    ok: user =\u003e $\"Found user: {user.Name}\",\n    err: error =\u003e error switch\n    {\n        ApiError.NotFound =\u003e \"User not found\",\n        ApiError.Unauthorized =\u003e \"Please login first\",\n        ApiError.ServerError =\u003e \"Server error occurred\",\n        _ =\u003e \"Unknown error\"\n    }\n);\n```\n\n### Working with Void Returns\n\nUse `Unit` as a return type for operations that don't return a value:\n\n```csharp\n// For methods that don't return a value\nResult\u003cUnit\u003e SaveData(string data)\n{\n    try\n    {\n        // Save data to database\n        File.WriteAllText(\"data.txt\", data);\n        return Result.Ok(Unit.Value);\n    }\n    catch (Exception ex)\n    {\n        return Result.Err\u003cUnit\u003e(ex);\n    }\n}\n\n// Usage\nResult\u003cUnit\u003e saveResult = SaveData(\"important data\");\nif (saveResult.IsOk)\n{\n    Console.WriteLine(\"Data saved successfully\");\n}\n```\n\n### Try-Catch Alternative\n\nUse `Result.From` to automatically catch exceptions:\n\n```csharp\n// Automatically catches exceptions\nResult\u003cint\u003e divideResult = Result.From(() =\u003e 10 / 0);\n// divideResult will be an Err with DivideByZeroException\n\n// For void-returning methods\nResult\u003cUnit\u003e writeResult = Result.From(() =\u003e File.WriteAllText(\"file.txt\", \"content\"));\n\n// With custom error mapping\nResult\u003cint, string\u003e customErrorResult = Result.From\u003cint, string\u003e(\n    () =\u003e ParseComplexData(),\n    ex =\u003e $\"Parsing error: {ex.Message}\"\n);\n```\n\n### Chaining Multiple Operations\n\n```csharp\nResult\u003cstring\u003e GetProcessedData(string input)\n{\n    return Result.Ok(input)\n        .AndThen(ValidateInput)\n        .Map(data =\u003e data.ToUpper())\n        .AndThen(ProcessData)\n        .Inspect(data =\u003e Console.WriteLine($\"Processing complete: {data}\"))\n        .InspectErr(ex =\u003e Console.WriteLine($\"Error occurred: {ex.Message}\"));\n}\n\nResult\u003cstring\u003e ValidateInput(string input)\n{\n    return string.IsNullOrEmpty(input)\n        ? Result.Err\u003cstring\u003e(\"Input cannot be empty\")\n        : Result.Ok(input);\n}\n\nResult\u003cstring\u003e ProcessData(string data)\n{\n    // Process the data\n    return Result.Ok($\"Processed: {data}\");\n}\n```\n\n### Combining Multiple Results\n\n```csharp\n// Combine multiple results into one\nResult\u003c(int, string, bool)\u003e combined = Result.Combine(\n    Result.Ok(42),\n    Result.Ok(\"hello\"),\n    Result.Ok(true)\n);\n\n// If any result is an error, the combined result will be an error\nResult\u003c(int, string)\u003e partialError = Result.Combine(\n    Result.Ok(42),\n    Result.Err\u003cstring\u003e(\"Something went wrong\")\n); // Will be Err with \"Something went wrong\"\n\n// Combine a collection of results\nIEnumerable\u003cResult\u003cint\u003e\u003e results = new[] { Result.Ok(1), Result.Ok(2), Result.Ok(3) };\nResult\u003cIEnumerable\u003cint\u003e\u003e collectionResult = Result.Combine(results);\n// Result.Ok([1, 2, 3])\n```\n\n## Asynchronous Operations\n\nSharpResults provides first-class support for asynchronous operations:\n\n```csharp\n// Async methods returning Result\npublic async Task\u003cResult\u003cUser\u003e\u003e GetUserAsync(int id)\n{\n    try\n    {\n        var user = await _userRepository.GetByIdAsync(id);\n        return user != null\n            ? Result.Ok(user)\n            : Result.Err\u003cUser\u003e(\"User not found\");\n    }\n    catch (Exception ex)\n    {\n        return Result.Err\u003cUser\u003e(ex);\n    }\n}\n\n// Chaining async operations\npublic async Task\u003cResult\u003cOrderConfirmation\u003e\u003e PlaceOrderAsync(int userId, int productId)\n{\n    return await GetUserAsync(userId)\n        .MapAsync(async user =\u003e {\n            // Validate user can place orders\n            if (!user.CanPlaceOrders)\n                return Result.Err\u003cUser\u003e(\"User cannot place orders\");\n            return Result.Ok(user);\n        })\n        .AndThenAsync(async user =\u003e await GetProductAsync(productId))\n        .AndThenAsync(async product =\u003e {\n            if (!product.InStock)\n                return Result.Err\u003cOrderConfirmation\u003e(\"Product out of stock\");\n                \n            var confirmation = await _orderService.CreateOrderAsync(userId, productId);\n            return Result.Ok(confirmation);\n        });\n}\n\n// Using LINQ with async results\npublic async Task\u003cResult\u003cOrderSummary\u003e\u003e GetOrderSummaryAsync(int orderId)\n{\n    var orderResult = await GetOrderAsync(orderId);\n    \n    return await (from order in orderResult\n                 from customer in await GetCustomerAsync(order.CustomerId)\n                 from items in await GetOrderItemsAsync(orderId)\n                 select new OrderSummary\n                 {\n                     OrderId = order.Id,\n                     CustomerName = customer.Name,\n                     Items = items,\n                     Total = items.Sum(i =\u003e i.Price)\n                 });\n}\n```\n\n## Real-World Examples\n\n### Web API Controller\n\n```csharp\n[ApiController]\n[Route(\"api/[controller]\")]\npublic class UsersController : ControllerBase\n{\n    private readonly IUserService _userService;\n    \n    public UsersController(IUserService userService)\n    {\n        _userService = userService;\n    }\n    \n    [HttpGet(\"{id}\")]\n    public async Task\u003cIActionResult\u003e GetUser(int id)\n    {\n        var result = await _userService.GetUserAsync(id);\n        \n        return result.Match\u003cIActionResult\u003e(\n            ok: user =\u003e Ok(user),\n            err: ex =\u003e ex.Message == \"User not found\" \n                ? NotFound(ex.Message) \n                : StatusCode(500, \"An error occurred while processing your request\")\n        );\n    }\n    \n    [HttpPost]\n    public async Task\u003cIActionResult\u003e CreateUser(CreateUserRequest request)\n    {\n        var result = await _userService.CreateUserAsync(request);\n        \n        return result.Match\u003cIActionResult\u003e(\n            ok: user =\u003e CreatedAtAction(nameof(GetUser), new { id = user.Id }, user),\n            err: ex =\u003e BadRequest(ex.Message)\n        );\n    }\n}\n```\n\n### Domain Logic with Validation\n\n```csharp\npublic class OrderService\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly IProductRepository _productRepository;\n    private readonly ICustomerRepository _customerRepository;\n    \n    // Constructor with DI\n    \n    public async Task\u003cResult\u003cOrder\u003e\u003e PlaceOrderAsync(OrderRequest request)\n    {\n        // Validate request\n        var validationResult = ValidateOrderRequest(request);\n        if (validationResult.IsErr)\n            return Result.Err\u003cOrder\u003e(validationResult.Exception);\n            \n        // Check if customer exists\n        var customerResult = await _customerRepository.GetByIdAsync(request.CustomerId);\n        if (customerResult.IsErr)\n            return Result.Err\u003cOrder\u003e($\"Customer not found: {request.CustomerId}\");\n            \n        // Check if all products exist and are in stock\n        var productsResult = await CheckProductsAvailabilityAsync(request.Items);\n        if (productsResult.IsErr)\n            return productsResult.MapErr\u003cOrder\u003e();\n            \n        // Create order\n        var order = new Order\n        {\n            CustomerId = request.CustomerId,\n            Items = request.Items.Select(i =\u003e new OrderItem { ProductId = i.ProductId, Quantity = i.Quantity }).ToList(),\n            OrderDate = DateTime.UtcNow,\n            Status = OrderStatus.Pending\n        };\n        \n        // Save order\n        try\n        {\n            await _orderRepository.CreateAsync(order);\n            return Result.Ok(order);\n        }\n        catch (Exception ex)\n        {\n            return Result.Err\u003cOrder\u003e(ex);\n        }\n    }\n    \n    private Result\u003cUnit\u003e ValidateOrderRequest(OrderRequest request)\n    {\n        if (request == null)\n            return Result.Err\u003cUnit\u003e(\"Order request cannot be null\");\n            \n        if (request.CustomerId \u003c= 0)\n            return Result.Err\u003cUnit\u003e(\"Invalid customer ID\");\n            \n        if (request.Items == null || !request.Items.Any())\n            return Result.Err\u003cUnit\u003e(\"Order must contain at least one item\");\n            \n        if (request.Items.Any(i =\u003e i.Quantity \u003c= 0))\n            return Result.Err\u003cUnit\u003e(\"All items must have a quantity greater than zero\");\n            \n        return Result.Ok(Unit.Value);\n    }\n    \n    private async Task\u003cResult\u003cUnit\u003e\u003e CheckProductsAvailabilityAsync(IEnumerable\u003cOrderItemRequest\u003e items)\n    {\n        foreach (var item in items)\n        {\n            var productResult = await _productRepository.GetByIdAsync(item.ProductId);\n            if (productResult.IsErr)\n                return Result.Err\u003cUnit\u003e($\"Product not found: {item.ProductId}\");\n                \n            var product = productResult.Value;\n            if (product.StockQuantity \u003c item.Quantity)\n                return Result.Err\u003cUnit\u003e($\"Insufficient stock for product {product.Name}. Available: {product.StockQuantity}, Requested: {item.Quantity}\");\n        }\n        \n        return Result.Ok(Unit.Value);\n    }\n}\n```\n\n## Performance Considerations\n\nSharpResults is designed to be lightweight and efficient. Here are some performance considerations:\n\n- **Avoid exceptions for control flow**: Using SharpResults instead of throwing exceptions can significantly improve performance in error-prone code paths.\n- **Lazy error handling**: Error messages and exceptions are only created when needed.\n- **Minimal allocations**: The library minimizes heap allocations where possible.\n- **Struct-based implementation**: For performance-critical code, consider using the struct-based variants of Result types.\n\n## Comparison with Other Libraries\n\n### ⚖️ TL;DR — Summary\n\n| Feature / Library | SharpResults | FluentResults | OneOf | CSharpFunctionalExtensions |\n|------------------|----------------------|---------------|-------|-----------------------------|\n| ✅ Strong Result/Error typing | ✔️ Yes | ✔️ Yes | ❌ No | ✔️ Yes |\n| ✅ Rich extension methods | ✔️ Yes | ✔️ Partial | ❌ Minimal | ✔️ Yes |\n| ✅ Exception capturing | ✔️ Yes | ✔️ Yes | ❌ No | ✔️ Yes |\n| ✅ Pattern matching support | ✔️ With Match | ✔️ With ResultType | ✔️ Native | ❌ Manual |\n| ✅ Null safety / value handling | ✔️ Yes | ✔️ Yes | ⚠️ Riskier | ✔️ Yes |\n| ✅ Simplicity \u0026 minimalism | ✔️ Lean and clean | ❌ Heavy | ✔️ Minimal | ❌ Verbose |\n| ✅ Control over error types | ✔️ Generic errors (TError) | ✔️ Message objects | ❌ Not applicable | ✔️ Generic |\n| 🛠️ IDE-friendliness (C# tooling) | ✔️ Yes | ✔️ Yes | ⚠️ Limited | ✔️ Yes |\n\n### Detailed Feature Comparison\n\n| Feature | SharpResults | OneOf | CSharpFunctionalExtensions | FluentResults |\n|---------|-------------|-------|----------------------------|---------------|\n| Custom Error Types | ✅ | ✅ | ✅ | ✅ |\n| LINQ Support | ✅ | ❌ | ✅ | ❌ |\n| Async Support | ✅ | ❌ | ✅ | ✅ |\n| Pattern Matching | ✅ | ✅ | ❌ | ❌ |\n| Implicit Conversions | ✅ | ✅ | ✅ | ❌ |\n| Deconstruction | ✅ | ✅ | ✅ | ❌ |\n| Multiple Error Collection | ✅ | ❌ | ❌ | ✅ |\n| Zero Dependencies | ✅ | ✅ | ✅ | ✅ |\n| .NET Standard 2.0+ | ✅ | ✅ | ✅ | ✅ |\n\n## Contributing\n\nContributions are welcome! Here's how you can contribute:\n\n1. **Fork the repository**\n2. **Create a feature branch**: `git checkout -b feature/amazing-feature`\n3. **Commit your changes**: `git commit -m 'Add some amazing feature'`\n4. **Push to the branch**: `git push origin feature/amazing-feature`\n5. **Open a Pull Request**\n\n### Development Guidelines\n\n- Follow the existing code style and conventions\n- Add unit tests for new features\n- Update documentation for any changes\n- Ensure all tests pass before submitting a PR\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafwa1%2Fsharpresults","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsafwa1%2Fsharpresults","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsafwa1%2Fsharpresults/lists"}