DriftReport.java
package network.ike.plugin.ws.reconcile;
import java.util.List;
/**
* Report from a {@link Reconciler#detect} call describing what (if
* anything) the reconciler would change on {@code scaffold-publish}.
*
* <p>The {@code optOutCommand} is rendered inline beside the
* dimension in {@code scaffold-draft} output so users can copy-paste
* to opt out without first looking up the flag name elsewhere.
*
* @param dimension human-readable dimension name
* @param hasDrift true if reconciliation would change state
* @param summary one-line drift summary (empty when no drift)
* @param detailLines additional context lines (empty list ok)
* @param defaultAction one-line description of what apply would do
* @param optOutCommand exact copy-paste command to skip this dimension
*/
public record DriftReport(
String dimension,
boolean hasDrift,
String summary,
List<String> detailLines,
String defaultAction,
String optOutCommand) {
/**
* Convenience constructor for the "no drift" case.
*
* @param dimension the dimension name
* @return a report with {@code hasDrift = false}
*/
public static DriftReport noDrift(String dimension) {
return new DriftReport(dimension, false, "", List.of(), "", "");
}
/**
* Render this report as a Markdown fragment for a goal report
* file (IKE-Network/ike-issues#407).
*
* <p>Shared renderer so reconciler drift looks the same in every
* report that includes it: a checkmark line when there is no
* drift, otherwise the dimension, summary, detail lines, default
* action, and the copy-paste opt-out command.
*
* @return a Markdown fragment, newline-terminated
*/
public String toMarkdown() {
if (!hasDrift) {
return "- ✓ **" + dimension + "** — no drift\n";
}
StringBuilder sb = new StringBuilder();
sb.append("- ⚠ **").append(dimension).append("**");
if (!summary.isEmpty()) {
sb.append(" — ").append(summary);
}
sb.append("\n");
for (String line : detailLines) {
sb.append(" - ").append(line).append("\n");
}
if (!defaultAction.isEmpty()) {
sb.append(" - _Default:_ ").append(defaultAction).append("\n");
}
if (!optOutCommand.isEmpty()) {
sb.append(" - _Opt out:_ `").append(optOutCommand).append("`\n");
}
return sb.toString();
}
/**
* Render this report as a Markdown fragment describing what a
* {@code scaffold-publish} reconciler <em>applied</em> — the
* past-tense counterpart of {@link #toMarkdown}
* (IKE-Network/ike-issues#431).
*
* <p>A reconciler's {@link Reconciler#apply} does exactly what its
* {@link Reconciler#detect} reported when the two are called back
* to back on an unchanged workspace, so the same {@code detailLines}
* (e.g. {@code doc-example: ike-parent:66 → 67}) describe the
* applied changes. The drift {@code summary}, {@code defaultAction},
* and {@code optOutCommand} are future-tense and omitted here.
*
* @return a Markdown fragment, newline-terminated
*/
public String toAppliedMarkdown() {
if (!hasDrift) {
return "- ✓ **" + dimension
+ "** — already current, nothing to apply\n";
}
StringBuilder sb = new StringBuilder();
sb.append("- ✓ **").append(dimension).append("** — applied ")
.append(detailLines.size()).append(" change(s)\n");
for (String line : detailLines) {
sb.append(" - ").append(line).append("\n");
}
return sb.toString();
}
}