View Javadoc
1   package com.guinetik.hexafun.hexa;
2   
3   import com.guinetik.hexafun.fun.Result;
4   import java.util.ArrayList;
5   import java.util.List;
6   
7   /**
8    * Builder step for defining validation and handler logic.
9    *
10   * <p>Supports chaining multiple validators that execute in order:
11   * <pre class="language-java">{@code
12   * .useCase(Keys.ADD)
13   *     .validate(Validators::validateCounter)
14   *     .validate(Validators::validateAmount)
15   *     .handle(input -> ...)
16   * }</pre>
17   *
18   * @param <I> The input type of the use case
19   */
20  public class UseCaseValidationStep<I> {
21  
22      private final String name;
23      private final UseCaseBuilder builder;
24      private final List<ValidationPort<I>> validators;
25  
26      public UseCaseValidationStep(
27          String name,
28          UseCaseBuilder builder,
29          ValidationPort<I> validator
30      ) {
31          this.name = name;
32          this.builder = builder;
33          this.validators = new ArrayList<>();
34          this.validators.add(validator);
35      }
36  
37      private UseCaseValidationStep(
38          String name,
39          UseCaseBuilder builder,
40          List<ValidationPort<I>> validators
41      ) {
42          this.name = name;
43          this.builder = builder;
44          this.validators = validators;
45      }
46  
47      /**
48       * Add another validator to the chain.
49       * Validators execute in order; first failure short-circuits.
50       *
51       * @param validator The next validation port
52       * @return This step for further chaining
53       */
54      public UseCaseValidationStep<I> validate(ValidationPort<I> validator) {
55          List<ValidationPort<I>> newValidators = new ArrayList<>(
56              this.validators
57          );
58          newValidators.add(validator);
59          return new UseCaseValidationStep<>(name, builder, newValidators);
60      }
61  
62      /**
63       * Define the handler that runs after all validators pass.
64       *
65       * @param handler The use case logic that processes the validated input
66       * @param <O> The output type of the handler
67       * @return The builder for chaining more use cases
68       */
69      public <O> UseCaseBuilder handle(UseCase<I, Result<O>> handler) {
70          ValidationPort<I> composedValidator = composeValidators();
71  
72          UseCase<I, Result<O>> useCase = input -> {
73              Result<I> validationResult = composedValidator.validate(input);
74              if (validationResult.isFailure()) {
75                  return Result.fail(validationResult.error());
76              }
77              return handler.apply(validationResult.get());
78          };
79  
80          builder.stage(name, useCase);
81          return builder;
82      }
83  
84      private ValidationPort<I> composeValidators() {
85          return input -> {
86              Result<I> result = Result.ok(input);
87              for (ValidationPort<I> validator : validators) {
88                  result = result.flatMap(validator::validate);
89                  if (result.isFailure()) {
90                      return result;
91                  }
92              }
93              return result;
94          };
95      }
96  }