001package gudusoft.gsqlparser.ir.semantic.diff;
002
003/**
004 * Pure-function return type for both projectors. {@link #getReason()} is
005 * non-null when the projector cannot produce a meaningful canonical model.
006 * The reporter translates a non-null reason into a single query-wide
007 * {@link DivergenceClass#UNSUPPORTED_BY_DLINEAGE} or
008 * {@link DivergenceClass#UNSUPPORTED_BY_IR} divergence.
009 *
010 * <p>Keeping this as data (not a thrown exception) means the comparison
011 * harness can produce a stable JSON for every corpus SQL even when one
012 * side is unsupported.
013 */
014public final class ProjectorResult {
015
016    public enum UnsupportedReason {
017        /** No relationships in the dlineage XML, or builder produced no statements. */
018        NO_RELATIONSHIPS,
019        /** Dlineage XML had zero or two-plus terminal select_list resultsets. */
020        MULTIPLE_TERMINAL_SELECTS,
021        /** Dlineage XML failed to parse. */
022        MALFORMED_XML
023    }
024
025    private final CanonicalLineageModel model;
026    private final UnsupportedReason reason;
027    private final String detail;
028
029    public ProjectorResult(CanonicalLineageModel model, UnsupportedReason reason, String detail) {
030        if (model == null) {
031            throw new IllegalArgumentException("model must not be null");
032        }
033        this.model = model;
034        this.reason = reason;
035        this.detail = detail;
036    }
037
038    public static ProjectorResult ok(CanonicalLineageModel model) {
039        return new ProjectorResult(model, null, null);
040    }
041
042    public static ProjectorResult unsupported(UnsupportedReason reason, String detail) {
043        if (reason == null) {
044            throw new IllegalArgumentException("reason must not be null for unsupported result");
045        }
046        return new ProjectorResult(CanonicalLineageModel.empty(), reason, detail);
047    }
048
049    public CanonicalLineageModel getModel() {
050        return model;
051    }
052
053    public UnsupportedReason getReason() {
054        return reason;
055    }
056
057    public String getDetail() {
058        return detail;
059    }
060
061    public boolean isSupported() {
062        return reason == null;
063    }
064}