UseCaseValidationStep.java
package com.guinetik.hexafun.hexa;
import com.guinetik.hexafun.fun.Result;
import java.util.ArrayList;
import java.util.List;
/**
* Builder step for defining validation and handler logic.
*
* <p>Supports chaining multiple validators that execute in order:
* <pre class="language-java">{@code
* .useCase(Keys.ADD)
* .validate(Validators::validateCounter)
* .validate(Validators::validateAmount)
* .handle(input -> ...)
* }</pre>
*
* @param <I> The input type of the use case
*/
public class UseCaseValidationStep<I> {
private final String name;
private final UseCaseBuilder builder;
private final List<ValidationPort<I>> validators;
public UseCaseValidationStep(
String name,
UseCaseBuilder builder,
ValidationPort<I> validator
) {
this.name = name;
this.builder = builder;
this.validators = new ArrayList<>();
this.validators.add(validator);
}
private UseCaseValidationStep(
String name,
UseCaseBuilder builder,
List<ValidationPort<I>> validators
) {
this.name = name;
this.builder = builder;
this.validators = validators;
}
/**
* Add another validator to the chain.
* Validators execute in order; first failure short-circuits.
*
* @param validator The next validation port
* @return This step for further chaining
*/
public UseCaseValidationStep<I> validate(ValidationPort<I> validator) {
List<ValidationPort<I>> newValidators = new ArrayList<>(
this.validators
);
newValidators.add(validator);
return new UseCaseValidationStep<>(name, builder, newValidators);
}
/**
* Define the handler that runs after all validators pass.
*
* @param handler The use case logic that processes the validated input
* @param <O> The output type of the handler
* @return The builder for chaining more use cases
*/
public <O> UseCaseBuilder handle(UseCase<I, Result<O>> handler) {
ValidationPort<I> composedValidator = composeValidators();
UseCase<I, Result<O>> useCase = input -> {
Result<I> validationResult = composedValidator.validate(input);
if (validationResult.isFailure()) {
return Result.fail(validationResult.error());
}
return handler.apply(validationResult.get());
};
builder.stage(name, useCase);
return builder;
}
private ValidationPort<I> composeValidators() {
return input -> {
Result<I> result = Result.ok(input);
for (ValidationPort<I> validator : validators) {
result = result.flatMap(validator::validate);
if (result.isFailure()) {
return result;
}
}
return result;
};
}
}