java.lang.Object
io.github.brantunger.unruly.api.RunResult<O>
Type Parameters:
O - The type of the output object

public final class RunResult<O> extends Object
What one run did: the output object, the rules that fired, what each rule's condition evaluated to, the checksum of the rules it used, and the tags and start time that chose which of them it used. Returned by RulesEngine.runWithResult(FactStore), and given to RuleListener.afterRun(RunContext, RunResult).

A run of an engine's own rules returns a result whose output() is null exactly when firedRules() is empty, which happens when the rule list is empty or no rule matched. A failed run throws instead of returning a partial result.

It's a final class rather than a record, so a later 2.x release can add accessors without breaking code compiled against this one. equals compares every field except startedAt(); a field added in a later release takes part too. Leaving out the start lets results compare equal whatever the engine's clock, such as a test double's result and a real engine's from a run without tags, when their other fields are equal too. The output is compared with its own equals, so an array output is compared by reference. The evaluations are compared as RuleEvaluation compares them: by rule and outcome, not by detail.

The output takes part in hashCode too, so a result whose output is changed afterwards, such as a map an action filled, no longer hashes the same: don't use a result as a key in a hash-based collection while its output can change.

  • Method Details

    • of

      public static <O> RunResult<O> of(@Nullable O output, List<Rule> firedRules, String ruleSetChecksum)
      Creates a result without evaluations. The engine creates its own; this is for a class that implements RulesEngine, such as a decorator or a test double. Its evaluations() and tags() are empty and its startedAt() is null; use of(Object, List, List, String) to report the evaluations. RunContext is sealed, its one implementation is internal to the engine, and the engine creates the contexts it passes to listeners, so an implementation carries a run's tags and start with withRun(RunContext) and a context an engine gave it, such as one its listener received.
      Type Parameters:
      O - The type of the output object
      Parameters:
      output - The output object, or null if no rule fired
      firedRules - The rules whose actions ran, in firing order; copied
      ruleSetChecksum - The checksum of the rules the run used
      Returns:
      The result
      Throws:
      NullPointerException - if firedRules, one of its elements, or ruleSetChecksum is null
    • of

      public static <O> RunResult<O> of(@Nullable O output, List<Rule> firedRules, List<RuleEvaluation> evaluations, String ruleSetChecksum)
      Creates a result. The engine creates its own; this is for a class that implements RulesEngine, such as a decorator or a test double. Its tags() are empty and its startedAt() is null. RunContext is sealed, its one implementation is internal to the engine, and the engine creates the contexts it passes to listeners, so an implementation carries a run's tags and start with withRun(RunContext) and a context an engine gave it, such as one its listener received.
      Type Parameters:
      O - The type of the output object
      Parameters:
      output - The output object, or null if no rule fired
      firedRules - The rules whose actions ran, in firing order; copied
      evaluations - What each rule's condition evaluated to, in evaluation order; copied
      ruleSetChecksum - The checksum of the rules the run used
      Returns:
      The result
      Throws:
      NullPointerException - if firedRules, evaluations, one of their elements, or ruleSetChecksum is null
    • withRun

      public RunResult<O> withRun(RunContext run)
      Returns a copy of this result that carries run's tags and start; everything else is the same. The engine calls it on the result of each of its runs before RuleListener.afterRun(RunContext, RunResult) sees it. RunContext is sealed, its one implementation is internal to the engine, and the engine creates the contexts it passes to listeners, so another implementation of RulesEngine calls it with a context an engine gave it, such as one its listener received.
      Parameters:
      run - The run whose tags and start the copy carries
      Returns:
      The copy
      Throws:
      NullPointerException - if run is null
    • output

      public @Nullable O output()
      Returns the output object the run produced.
      Returns:
      The output, or null if no rule fired
    • firedRules

      public List<Rule> firedRules()
      Returns the rules whose actions ran, in firing order. An engine that fires the first match returns at most one.
      Returns:
      The fired rules; unmodifiable, and empty if none fired
    • evaluations

      public List<RuleEvaluation> evaluations()
      Returns what each rule's condition evaluated to: one evaluation for every loaded rule, in evaluation order, the same Rule instances as RuleSetInfo.rules(). On a first-match engine, the rules after the match are RuleEvaluation.Outcome.NOT_EVALUATED; to know whether they would have matched, use an all-matches or a unique-match engine, which evaluate every condition of the rules the run uses. A rule the run skipped is RuleEvaluation.Outcome.SKIPPED on every engine. A result created with of(Object, List, String) has none.
      Returns:
      The evaluations; unmodifiable, and empty if the rule list is empty
    • ruleSetChecksum

      public String ruleSetChecksum()
      Returns the checksum of the rules this run used. During a reload it can differ from the engine's current RuleSetInfo.checksum(), because a run finishes with the rules it started with.
      Returns:
      The checksum, as RuleSetInfo.checksum() describes it
    • tags

      public Set<String> tags()
      Returns the tags the run was given, as RunContext.tags() describes them: the run used only the rules that carry at least one of them.
      Returns:
      The tags, in String order; unmodifiable, and empty if the run used every rule or the result was created with of(...) and not given a run
    • startedAt

      public @Nullable Instant startedAt()
      Returns when the run started, by the engine's clock, as RunContext.startedAt() describes it: the instant that judged every rule's validity window. A fixed clock gives every run the same instant. It isn't comparable to RuleSetInfo.loadedAt() or the run's deadline.
      Returns:
      The instant, or null if the result was created with of(...) and not given a run
    • equals

      public boolean equals(@Nullable Object other)
      Compares every field except startedAt(), which the class description explains.
      Overrides:
      equals in class Object
      Parameters:
      other - The object to compare with
      Returns:
      Whether other is a result with an equal output, fired rules, evaluations, checksum and tags, whenever each run started
    • hashCode

      public int hashCode()
      Combines the hash codes of the output, the checksum and the tags with the number of fired rules, consistently with equals(Object). It hashes only these, so that it doesn't walk every evaluation.
      Overrides:
      hashCode in class Object
      Returns:
      The hash code
    • toString

      public String toString()
      Describes the result, such as RunResult(output={rate=4.5}, firedRules=[prime-rate], evaluations=[prime-rate=MATCHED], ruleSetChecksum=9f2c..., tags=[], startedAt=2027-06-01T00:00:00Z). Rule names and tags are shortened to 200 characters, then escaped, as the engine's error messages show names.
      Overrides:
      toString in class Object
      Returns:
      The description