Interface Guard<T>

Type Parameters:
T - the type of value being validated
Functional Interface:
This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

@FunctionalInterface @NullMarked public interface Guard<T>
A named, composable predicate that produces a Validated result when applied to a value.

Guard<T> is a functional interface whose single abstract method is check(T), which returns Validated<NonEmptyList<String>, T>: Valid(value) when the predicate passes, or Invalid(errors) when it fails.

Guards are designed to be defined once and reused across validation pipelines, eliminating the repetitive if/Validated.invalidNel(Object) pattern:

Guard<String> notBlank     = Guard.of(s -> !s.isBlank(),         "must not be blank");
Guard<String> minLength3   = Guard.of(s -> s.length() >= 3,      "must be at least 3 chars");
Guard<String> alphanumeric = Guard.of(s -> s.matches("[\\w]+"),  "must be alphanumeric");

Guard<String> username = notBlank.and(minLength3).and(alphanumeric);

username.check("al");  // Invalid(["must be at least 3 chars"])
username.check("ok?"); // Invalid(["must be alphanumeric"])
username.check("alice"); // Valid("alice")

Composition semantics

  • and — both guards must pass; errors from all failing guards are accumulated (not fail-fast).
  • or — the first passing guard short-circuits; if all fail, all errors are accumulated.
  • negate / negate(message) — inverts the predicate.
  • Method Summary

    Modifier and Type
    Method
    Description
    default Guard<T>
    and(Guard<T> other)
    Returns a composed guard that requires both this guard and other to pass.
    default Predicate<T>
    Returns a standard Predicate<T> that returns true when this guard passes and false when it fails.
    check(T value)
    Applies this guard to value.
    default Option<T>
    Applies this guard to value and returns an Option<T>.
    Applies this guard to value and returns a Result<T, NonEmptyList<String>>.
    default <E> Result<T,E>
    checkToResult(T value, Function<NonEmptyList<String>, E> toError)
    Applies this guard to value and returns a Result<T, E>, mapping the accumulated error list to a domain-specific error type via toError.
    default <U> Guard<U>
    contramap(Function<? super U, ? extends T> mapper)
    Returns a Guard<U> that applies mapper to its input before checking.
    default Guard<T>
    Returns a guard that is the logical negation of this guard, using a generic error message.
    default Guard<T>
    negate(String errorMessage)
    Returns a guard that is the logical negation of this guard, using the supplied error message when the original guard passes.
    static <T> Guard<T>
    of(Predicate<? super T> predicate, String errorMessage)
    Creates a Guard<T> from a predicate and a static error message.
    static <T> Guard<T>
    of(Predicate<? super T> predicate, Function<? super T, String> errorMessageFn)
    Creates a Guard<T> from a predicate and a dynamic error message function.
    default Guard<T>
    or(Guard<T> other)
    Returns a composed guard that passes when at least one of this guard or other passes.
  • Method Details

    • check

      Validated<NonEmptyList<String>, T> check(T value)
      Applies this guard to value.
      Parameters:
      value - the value to validate; must not be null
      Returns:
      Valid(value) if the predicate passes, or Invalid(errors) if it fails
    • of

      static <T> Guard<T> of(Predicate<? super T> predicate, String errorMessage)
      Creates a Guard<T> from a predicate and a static error message.

      Example:

      Guard<String> notBlank = Guard.of(s -> !s.isBlank(), "must not be blank");
      
      Type Parameters:
      T - the value type
      Parameters:
      predicate - the condition that must hold for the value to be valid
      errorMessage - the error message produced when the predicate fails
      Returns:
      a new Guard<T>
      Throws:
      NullPointerException - if predicate or errorMessage is null
    • of

      static <T> Guard<T> of(Predicate<? super T> predicate, Function<? super T, String> errorMessageFn)
      Creates a Guard<T> from a predicate and a dynamic error message function.

      The errorMessageFn receives the failing value so it can produce a context-specific message.

      Example:

      Guard<Integer> max = Guard.of(
          n -> n <= 100,
          n -> "must be ≤ 100, got " + n
      );
      
      Type Parameters:
      T - the value type
      Parameters:
      predicate - the condition that must hold for the value to be valid
      errorMessageFn - function that produces an error message from the failing value
      Returns:
      a new Guard<T>
      Throws:
      NullPointerException - if predicate or errorMessageFn is null
    • and

      default Guard<T> and(Guard<T> other)
      Returns a composed guard that requires both this guard and other to pass.

      Both guards are always evaluated — this is not fail-fast. Errors from all failing guards are accumulated into a single NonEmptyList, so the caller receives a complete picture of all violations at once.

      Example:

      Guard<Integer> positive = Guard.of(n -> n > 0,      "must be positive");
      Guard<Integer> even     = Guard.of(n -> n % 2 == 0, "must be even");
      Guard<Integer> positiveEven = positive.and(even);
      
      positiveEven.check(4);   // Valid(4)
      positiveEven.check(3);   // Invalid(["must be even"])
      positiveEven.check(-1);  // Invalid(["must be positive", "must be even"])
                               //  — both guards evaluated, both errors collected
      
      Parameters:
      other - the guard that must also pass; must not be null
      Returns:
      a composed Guard<T>
      Throws:
      NullPointerException - if other is null
    • or

      default Guard<T> or(Guard<T> other)
      Returns a composed guard that passes when at least one of this guard or other passes.

      Evaluation is short-circuit: if this guard passes, other is never evaluated. If both fail, errors from both guards are accumulated.

      Example:

      Guard<String> email = Guard.of(s -> s.contains("@"),  "must contain @");
      Guard<String> phone = Guard.of(s -> s.matches("\\d+"), "must be digits");
      Guard<String> contact = email.or(phone);
      
      contact.check("alice@example.com");  // Valid — email passes, phone not evaluated
      contact.check("12345");             // Valid — phone passes
      contact.check("hello");             // Invalid(["must contain @", "must be digits"])
      
      Parameters:
      other - the alternative guard; must not be null
      Returns:
      a composed Guard<T>
      Throws:
      NullPointerException - if other is null
    • negate

      default Guard<T> negate()
      Returns a guard that is the logical negation of this guard, using a generic error message.

      The composed guard returns Valid(value) when this guard fails, and Invalid(["must not satisfy the condition"]) when this guard passes. Use negate(message) to supply a domain-specific error message.

      Returns:
      the negated Guard<T>
    • negate

      default Guard<T> negate(String errorMessage)
      Returns a guard that is the logical negation of this guard, using the supplied error message when the original guard passes.

      Example:

      Guard<String> notAdmin = Guard.of(s -> s.equals("admin"), "is admin")
                                    .negate("username must not be 'admin'");
      
      notAdmin.check("alice"); // Valid("alice")
      notAdmin.check("admin"); // Invalid(["username must not be 'admin'"])
      
      Parameters:
      errorMessage - the error message returned when the original guard passes
      Returns:
      the negated Guard<T>
      Throws:
      NullPointerException - if errorMessage is null
    • asPredicate

      default Predicate<T> asPredicate()
      Returns a standard Predicate<T> that returns true when this guard passes and false when it fails.

      Use this to integrate guards with standard Java APIs that accept Predicate (e.g., Stream.filter, Collection.removeIf).

      Example:

      Guard<String> notBlank = Guard.of(s -> !s.isBlank(), "must not be blank");
      
      List<String> valid = Stream.of("alice", "  ", "bob", "")
          .filter(notBlank.asPredicate())
          .toList();
      // ["alice", "bob"]
      
      Returns:
      a Predicate<T> backed by this guard
    • contramap

      default <U> Guard<U> contramap(Function<? super U, ? extends T> mapper)
      Returns a Guard<U> that applies mapper to its input before checking.

      This is the contravariant map operation: it adapts a guard written for type T to work on an enclosing type U by projecting U → T first. It is the idiomatic way to reuse field-level guards on whole objects.

      Example:

      Guard<String> notBlank = Guard.of(s -> !s.isBlank(), "username must not be blank");
      
      // Lift notBlank to validate User objects by their username field
      Guard<User> userGuard = notBlank.contramap(User::username);
      
      userGuard.check(new User("alice")); // Valid(user)
      userGuard.check(new User("   "));   // Invalid(["username must not be blank"])
      
      Type Parameters:
      U - the input type of the returned guard
      Parameters:
      mapper - function that extracts the T value from a U; must not be null
      Returns:
      a new Guard<U> that projects U → T before checking
      Throws:
      NullPointerException - if mapper is null
    • checkToResult

      default Result<T, NonEmptyList<String>> checkToResult(T value)
      Applies this guard to value and returns a Result<T, NonEmptyList<String>>.

      Equivalent to this.check(value).toResult() but removes the need to import and chain the conversion manually.

      Parameters:
      value - the value to validate
      Returns:
      Result.ok(value) if the guard passes, or Result.err(errors) if it fails
    • checkToResult

      default <E> Result<T,E> checkToResult(T value, Function<NonEmptyList<String>, E> toError)
      Applies this guard to value and returns a Result<T, E>, mapping the accumulated error list to a domain-specific error type via toError.

      Use this at domain service boundaries where Result is the preferred container and the error type is richer than a plain list of strings.

      Example:

      Guard<String> username = notBlank.and(minLength3);
      
      Result<String, ValidationException> result = username.checkToResult(
          input,
          errors -> new ValidationException("username", errors.toList())
      );
      
      Type Parameters:
      E - the domain error type
      Parameters:
      value - the value to validate
      toError - function mapping the accumulated error list to E
      Returns:
      Result.ok(value) on success, or Result.err(toError(errors)) on failure
      Throws:
      NullPointerException - if toError is null
    • checkToOption

      default Option<T> checkToOption(T value)
      Applies this guard to value and returns an Option<T>.

      Returns Some(value) when the guard passes and None when it fails, discarding the error details. Use this when you only need to know whether a value is valid, not why it is not.

      Example:

      Guard<String> notBlank = Guard.of(s -> !s.isBlank(), "must not be blank");
      
      // Filter a stream keeping only valid values
      List<String> valid = Stream.of("alice", "  ", "bob")
          .flatMap(s -> notBlank.checkToOption(s).stream())
          .toList();
      // ["alice", "bob"]
      
      Parameters:
      value - the value to validate
      Returns:
      Option.some(value) if the guard passes, or Option.none() if it fails