Skip to content

Lightweight result wrapper and validation toolkit for C#. Brings clean Result<T> handling and extensible validation with custom attributes.

License

Notifications You must be signed in to change notification settings

HBartosch/Routya.ResultKit

Repository files navigation

CI CI NuGet NuGet .NET Standard Supports Nested Validation

📦 Routya.ResultKit

**Lightweight result wrapper, validation and transformation toolkit for C# **
Brings clean Result<T> handling and extensible validation with custom attributes.


✨ Features

  • ✅ Consistent Result<T> response pattern
  • ✅ One-line .Validate() extension for request models
  • .Transform() extension for clean and safe object/result projection
  • ✅ Rich built-in and custom validation attributes
  • ✅ Works great with System.ComponentModel.Annotations
  • ✅ Validation for nested objects

📥 Installation

dotnet add package Routya.ResultKit --version 1.0.2

🚀 Quick Start

1. Define Your Request Model (with Custom Validation)

using Routya.ResultKit.Attributes;
using System.ComponentModel.DataAnnotations;

  private enum UserRole { Admin, User, Guest }

    private class TestModel
    {
        [Required]
        public string Name { get; set; }

        [EmailAddress]
        public string Email { get; set; }

        [Range(18, 120)]
        public int Age { get; set; }

        [StringEnum(typeof(UserRole))]
        public string Role { get; set; }

        [Required]
        public string Password { get; set; }

        [Compare("Password")]
        public string ConfirmPassword { get; set; }

        public decimal MinPurchase { get; set; }

        [GreaterThan("MinPurchase")]
        public decimal MaxPurchase { get; set; }
    }

2. Validate and Return

app.MapPost("/users", (CreateUserRequest request) =>
{
    var validationResult = request.Validate();

    if (!validationResult.Success)
        return Results.BadRequest(validationResult);

    return Results.Ok(Result.Ok(new { Id = 1 }));
});

✅ Successful Response Example

{
	"Success": true,
	"Data": {
		"Name": "Henry",
		"Email": "[email protected]",
		"Age": 30,
		"Role": "Admin",
		"Password": "abc123",
		"ConfirmPassword": "abc123",
		"MinPurchase": 100.0,
		"MaxPurchase": 200.0
	},
	"Error": null
}

❌ Validation Error Response Example

{
	"Success": false,
	"Data": null,
	"Error": {
		"Title": "Validation Failed",
		"Status": 400,
		"Extensions": {
			"errors": {
				"Name": [
					"The Name field is required."
				],
				"Email": [
					"The Email field is not a valid e-mail address."
				],
				"Age": [
					"The field Age must be between 18 and 120."
				],
				"Role": [
					"Role must be one of: Admin, User, Guest"
				],
				"ConfirmPassword": [
					"'ConfirmPassword' and 'Password' do not match."
				],
				"MaxPurchase": [
					"MaxPurchase must be greater than MinPurchase"
				]
			}
		}
	}
}

🛠️ Built-in Validation Attributes

Routya.ResultKit includes powerful validation attributes ready to use:

Attribute Purpose
[GreaterThan("OtherProp")] Ensure a property is greater than another
[LessThan("OtherProp")] Ensure a property is less than another
[RequiredIf("OtherProp", "Value")] Conditionally require a property
[RequiredIfEmpty("OtherProp")] Require a property if another is empty
[StringEnum(typeof(EnumType))] Ensure a string matches an Enum name
[MatchRegex("pattern")] Validate a string against a regex
[MinItems(count)] Validate minimum items in a collection
[MaxItems(count)] Validate maximum items in a collection
[ValidStartEndDateRange("Start", "End")] Validate that StartDate is before EndDate (This is a class level attribute)
[ValidDateTimeOffsetRange("End")] Validate DateTimeOffset ranges
[ValidDateTimeRange("End")] Validate DateTime ranges


🔁 Transforming Models

Use .Transform(...) to reshape validated models or result data into domain entities or response objects — cleanly and safely.


✅ Example 1: Basic Object Transformation

var request = "Hello";

var greeting = request.Transform(str => new Greeting
{
    Message = str,
    Length = str.Length
});
public class Greeting
{
    public string Message { get; set; }
    public int Length { get; set; }
}

✅ Example 2: Full Validate → Transform → Result Flow

var result = request.Validate()
    .Transform(req => new CreateUserCommand
    {
        Name = req.Name,
        Email = req.Email,
        Role = Enum.Parse<UserRole>(req.Role, ignoreCase: true)
    });

✅ Example 3: Transforming Result Output

var result = Result.Ok(user)
    .Transform(u => new UserResponse
    {
        Id = u.Id,
        Name = u.Name
    });

🧠 Why Use Transform(...)?

Benefit Description
✅ Fluent Clean chaining after .Validate()
✅ Safe When using Result it only transforms data if result is successful
✅ Expressive Encourages intentional mapping logic
✅ Lightweight Zero dependencies, pure functional mapping

🔍 Bonus: Works with Both Objects and Result

TOut Transform<TIn, TOut>(this TIn input, Func<TIn, TOut> selector)

Result<TOut> Transform<TIn, TOut>(this Result<TIn> result, Func<TIn, TOut> selector)

About

Lightweight result wrapper and validation toolkit for C#. Brings clean Result<T> handling and extensible validation with custom attributes.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Sponsor this project

Languages