Skip to content

stevomccormack/PineGuard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

.NET 10.0 100% Coverage SonarQube Clean Roslyn Clean MIT License

PineGuard

Validation that thinks like you do.
330+ rules. 550+ Must clauses. 530+ Guard clauses. 13,000+ tests.
Built by AI. Loved by AI. Trusted by engineers.


Why PineGuard?

PineGuard gives you a single validation model you can reuse across every .NET boundary that matters:

  • Must.Be.*() when you want result-based validation you can compose
  • Guard.Against.*() when you want fail-fast guards and parsed return values
  • FluentValidation adapters when you want request validation that reads naturally
  • DataAnnotations attributes when you want model-level validation without duplicating rules

That means you don’t end up maintaining four different validation dialects for the same business rule.

Why teams choose PineGuard

  • One mental model, multiple delivery styles. Write validation once, then use it as a result, a guard, a FluentValidation rule, or a DataAnnotations attribute.
  • Broad coverage without a fragmented API. Strings, numbers, dates, collections, URIs, OWASP-safe input, network identifiers, and more.
  • Security built in. PineGuard includes real OWASP, URI, hostname, and reference-data validations instead of stopping at trivial string checks.
  • Exception policy without forking the library. Guard exceptions can stay default, be replaced globally, be scoped to an operation, or be overridden per call.

One validation model. Every .NET boundary.

Choose the surface that fits the call site

Surface Best when you want... Example
Must.Be.*() result-based validation and composable flow control Must.Be.Email(email)
Guard.Against.*() fail-fast validation and typed/parsed return values Guard.Against.NotHttpsUrl(callback)
PineGuard.FluentValidation request validators that read naturally in pipelines RuleFor(x => x.Website).WebUrl()
PineGuard.DataAnnotations attribute-driven validation on DTOs and models [WebUrl]

Quick Start

Install

# Core validation rules (zero dependencies)
dotnet add package PineGuard.Core

# Must clauses — result-based fluent validation
dotnet add package PineGuard.MustClauses

# Guard clauses — throw-on-failure guards
dotnet add package PineGuard.GuardClauses

# FluentValidation adapter
dotnet add package PineGuard.FluentValidation

# DataAnnotations attributes
dotnet add package PineGuard.DataAnnotations

Must Clauses — result-based, explicit, composable

Best for: APIs, services, pipelines, and places where you want to decide what happens next.

using PineGuard.MustClauses;

var emailResult = Must.Be.Email(email);
if (emailResult.Failed)
    return BadRequest(emailResult.Message);

var callbackUri = Must.Be.HttpsUrl(httpsCallback).OrThrow();
var safeInput = Must.Be.OwaspSafe(input).OrThrow();

Must.Be.Email(email).ThrowIfFailed((message, paramName) =>
    new BusinessException($"{paramName}: {message}"));

Guard Clauses — fail fast, and return parsed data when useful

Best for: application boundaries, constructors, service methods, and anywhere invalid input should stop immediately.

using PineGuard.GuardClauses;

public sealed record EndpointConfiguration(
    string DisplayName,
    Uri WebsiteUri,
    Uri CallbackUri,
    string Hostname,
    string SafeInput);

public sealed class EndpointService
{
    public EndpointConfiguration Create(
        string displayName,
        string website,
        string httpsCallback,
        string hostname,
        string input)
    {
        var name = Guard.Against.NotNull(displayName);
        var websiteUri = Guard.Against.NotUrl(website);           // accepts http:// or https:// and returns Uri
        var callbackUri = Guard.Against.NotHttpsUrl(httpsCallback); // returns Uri and enforces HTTPS only
        var host = Guard.Against.Hostname(hostname);             // domain-only, e.g. openai.com
        var safeInput = Guard.Against.OwaspUnsafe(input);

        return new EndpointConfiguration(name, websiteUri, callbackUri, host, safeInput);
    }
}

Guard names the forbidden state. That’s why PineGuard uses NotUrl(...) / NotHttpsUrl(...) on the Guard surface while still returning the parsed, valid result.

Guard Exception Policy — your domain, your exception story

Best for: teams who want a single validation library but need exceptions that match their application language.

using PineGuard.GuardClauses;
using PineGuard.MustClauses;

// Default behavior: built-in ArgumentException / ArgumentNullException
Guard.Against.NotNull(orderId);

// Global replacement
GuardExceptionPolicy.ExceptionReplacer = ex => new DomainValidationException(ex.Message, ex);
GuardExceptionPolicy.ReplaceDefaultExceptions = true;

Guard.Against.NotNull(orderId);

// Scoped replacement
using (GuardExceptionPolicy.BeginScope(options =>
{
    options.ExceptionReplacer = ex => new CheckoutException(ex.Message, ex);
    options.ReplaceDefaultExceptions = true;
}))
{
    Guard.Against.NotNull(orderId);
    Guard.Against.OwaspUnsafe(input);
}

// Per-call override wins for this invocation only
Guard.Against.NotNull(
    orderId,
    exceptionCreator: () => new CheckoutException("Order id is required."));

// If you're already working with MustResult<T>, throw from there instead
Must.Be.OwaspSafe(input).ThrowIfFailed((message, paramName) =>
    new CheckoutException($"{paramName}: {message}"));

FluentValidation Integration — expressive pipeline validation

Best for: application requests, commands, DTOs, and APIs that already use FluentValidation.

using FluentValidation;
using PineGuard.FluentValidation;

public sealed record CreateEndpointRequest(
    string? DisplayName,
    string? LegacyAlias,
    string? Website,
    string? HttpCallback,
    string? HttpsCallback,
    string? Hostname,
    string? Email,
    string? StrictEmailAddress,
    string? UserInput);

public sealed class CreateEndpointRequestValidator : AbstractValidator<CreateEndpointRequest>
{
    public CreateEndpointRequestValidator()
    {
        RuleFor(x => x.DisplayName).Required();       // Fluent-specific name to avoid NotNull()/Null() collisions
        RuleFor(x => x.LegacyAlias).NotRequired();    // value must be null

        RuleFor(x => x.Website).WebUrl();             // accepts http:// or https://
        RuleFor(x => x.HttpCallback).HttpUrl();       // HTTP only
        RuleFor(x => x.HttpsCallback).HttpsUrl();     // HTTPS only
        RuleFor(x => x.Hostname).Hostname();          // domain-only, e.g. openai.com

        RuleFor(x => x.Email).Required().Email();
        RuleFor(x => x.StrictEmailAddress).Required().StrictEmail();
        RuleFor(x => x.UserInput).Required().OwaspSafe();
    }
}

DataAnnotations — attribute-driven validation on models

Best for: DTOs, input models, MVC binding, and codebases that prefer declarative attributes.

using PineGuard.DataAnnotations;

public sealed class CreateEndpointRequest
{
    [NotNull]
    public string DisplayName { get; init; } = string.Empty;

    [Null] // explicit: this value must remain null
    public string? LegacyAlias { get; init; }

    [WebUrl] // accepts http:// or https://
    public string? Website { get; init; }

    [HttpUrl]
    public string? HttpCallback { get; init; }

    [HttpsUrl]
    public string? HttpsCallback { get; init; }

    [Hostname] // domain-only, e.g. openai.com
    public string? Hostname { get; init; }

    [NotNull]
    [Email]
    public string Email { get; init; } = string.Empty;

    [NotNull]
    [StrictEmail]
    public string StrictEmailAddress { get; init; } = string.Empty;

    [NotNull]
    [OwaspSafe]
    public string UserInput { get; init; } = string.Empty;
}

DataAnnotations format attributes allow null by default. Pair them with [NotNull] when the property must be present and valid.

FluentValidation uses Required() / NotRequired() for presence checks to avoid built-in naming collisions. DataAnnotations uses [NotNull] / [Null] for the equivalent presence semantics today.


About

.NET input validation library with over 70+ rules. Perfect for DDD, Clean Architecture & API validation. Built for engineers, mastered by AI

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages