Skip to content

chamook/Chamook.Validation

Repository files navigation

Chamook.Validation

./logo.png

This library helps you build types that protect their invariants by only allowing valid versions to be constructed.

Installation

dotnet add package Chamook.Validation

How-to

Namespace

using Chamook.Validation;

Validated<TValid, TError>

The Validated type represents the result of a validation operation. Only one of the values (either Valid or Error) can be present.

A new Validated object is created using one of the static methods corresponding to states:

var valid = Validated<CoolObject, string>.Valid(myCoolObject);
var invalid = Validated<CoolObject, string>.Error("Not cool enough");

The recommended way to extract a value is by using the Match method which requires a function to handle both possiblities:

valid.Match(
    ifValid: v => Console.WriteLine(v.ToString()),
    ifError: e => Console.WriteLine(e));

If the handlers return a value, they must both return the same type of value.

var outcome =
    valid.Match(
        ifValid: v => v.ToString(),
        ifError: e => e);

Constrained Type

A Constrained Type is a handy way to wrap an existing type with a simple validation rule. Validation rules are implemented as a class that implements the IConstraint<T> interface.

public interface IConstraint<T>
{
    bool IsValid(T candidate);
}

The single method IsValid defines validation rules for the type to be tested T.

Note that ConstrainedType requires an IConstraint to have a parameterless constructor.

With a constraint defined, a class can inherit from ConstrainedType, fill in the type parameters, and implement 2 methods to be complete. See the example below of NonEmptyString:

///<summary>
///A string that can't be empty or whitespace
///</summary>
public sealed class NonEmptyString: ConstrainedType<NonEmptyString.Constraint, string>
{
    public sealed class Constraint : IConstraint<string>
    {
        public Constraint() {}
        public bool IsValid(string candidate) => !string.IsNullOrWhiteSpace(candidate);
    }

    private NonEmptyString(string valid) : base(valid) {}
    public new static Validated<NonEmptyString, TError> Validate<TError>(
        string? candidate,
        Func<TError> errorCreator) =>
        DoValidate(candidate ?? "", errorCreator).Map(x => new NonEmptyString(x));
}

The constructor for NonEmptyString just calls the base constructor with a valid value.

The static Validate method passes through to the DoValidate method on the base class and maps the type of the result to NonEmptyString.

Note that NonEmptyString also handles null input values by coalescing to an empty string before validating (which will of course result in an error) - but handling null input is not strictly necessary.

Validate also requires that a simple function to create an appropriate error response is provided - this is to allow for better error messages in practice.

Combinations

Validation results can be combined together using the And method. This allows for many individual validation steps to be performed when attempting to build a larger class. Either all errors are collected or the Map method can be used to build the final result type. See the Sample below:

public sealed record Sample(
    NonEmptyString One,
    NonEmptyString Two,
    NonEmptyString Three)
{
    public static Validated<Sample, string> Validate(
        string? one,
        string? two,
        string? three) =>
        NonEmptyString.Validate(one, () => "One is required")
        .And(NonEmptyString.Validate(two, () => "Two is required"))
        .And(NonEmptyString.Validate(three, () => "Three is required"))
        .Map((x1, x2, x3) => new Sample(x1, x2, x3))
        .MapError(x => string.Join(", ", x));
}

Alternatives

💡 If you like to build rules on their own maybe FluentValidation is more your speed

💡 If you’re a big fan of annotations, you could try MiniValidation

License

This is licensed under the Apache License 2.0

About

A validation library using types for .NET

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages