import { stringify } from "./expressions";
import type { ExprValue } from "./expressions";
import type { JobSpec } from "./spec";
const MAX_MATRIX_COMBINATIONS = 64;
export function expandMatrix(spec: JobSpec): Array<Record<string, ExprValue>> {
const matrix = spec.strategy?.matrix;
if (!matrix) return [{}];
const { include, exclude, ...axes } = matrix;
let combos: Array<Record<string, ExprValue>> = [{}];
for (const [axis, values] of Object.entries(axes)) {
if (!Array.isArray(values)) continue;
combos = combos.flatMap((combo) => values.map((value) => ({ ...combo, [axis]: value })));
}
if (Object.keys(axes).length === 0) combos = [];
const matches = (combo: Record<string, ExprValue>, row: Record<string, ExprValue>) =>
Object.entries(row).every(([key, value]) => combo[key] === value);
combos = combos.filter((combo) => !(exclude ?? []).some((row) => matches(combo, row)));
for (const row of include ?? []) {
const target = combos.find((combo) =>
Object.keys(axes).every((axis) => !(axis in row) || combo[axis] === row[axis]),
);
if (target && Object.keys(row).some((key) => key in axes)) Object.assign(target, row);
else combos.push({ ...row });
}
if (combos.length > MAX_MATRIX_COMBINATIONS)
throw new Error(`matrix expands to more than ${MAX_MATRIX_COMBINATIONS} jobs`);
return combos.length > 0 ? combos : [{}];
}
export function matrixLabel(values: Record<string, ExprValue>): string {
return Object.values(values).map(stringify).join(", ");
}