1import type { ChartData, Metric, Metrics } from "../types/data.js";
2import type { Session } from "../wrappers/session.js";
3
4/**
5 * Helps with transforming benchmark data into something that can be visualized easily.
6 */
7export class Transforms {
8  private constructor() {
9    // static helpers.
10  }
11
12  static buildMetrics(session: Session, suppressed: Set<string>, suppressedMetrics: Set<string>): Metrics<number> {
13    const classGroups = Object.entries(session.classGroups);
14    const standard: Metric<number>[] = [];
15    const sampled: Metric<number[]>[] = [];
16    for (let i = 0; i < classGroups.length; i += 1) {
17      const [className, wrappers] = classGroups[i];
18      for (let j = 0; j < wrappers.length; j += 1) {
19        const wrapper = wrappers[j];
20        const datasetName = wrappers[j].value.datasetName();
21        if (suppressed.has(datasetName)) {
22          continue;
23        }
24        const source = wrapper.source;
25        const testName = wrapper.value.testName();
26        // standard
27        let labels = wrapper.value.metricLabels();
28        for (let k = 0; k < labels.length; k += 1) {
29          const label = labels[k];
30          if (suppressedMetrics.has(label)) {
31            continue;
32          }
33          const metric = wrapper.value.metric(label);
34          const charData: ChartData<number> = {
35            values: metric.runs
36          };
37          Transforms.add<number>(
38            standard,
39            className,
40            testName,
41            label,
42            source,
43            charData
44          );
45        }
46        // sampled
47        labels = wrapper.value.sampledLabels();
48        for (let k = 0; k < labels.length; k += 1) {
49          const label = labels[k];
50          if (suppressedMetrics.has(label)) {
51            continue;
52          }
53          const metric = wrapper.value.sampled(label);
54          const charData: ChartData<number[]> = {
55            values: metric.runs
56          };
57          Transforms.add<number[]>(
58            sampled,
59            className,
60            testName,
61            label,
62            source,
63            charData
64          );
65        }
66      }
67    }
68    const metrics: Metrics<number> = {
69      standard: standard,
70      sampled: sampled
71    };
72    return metrics;
73  }
74
75  private static add<T>(
76    metrics: Metric<T>[],
77    className: string,
78    testName: string,
79    label: string,
80    source: string,
81    data: ChartData<T>
82  ) {
83    const metric = Transforms.getOrCreate<T>(metrics, className, testName, label);
84    metric.data[source] = data;
85  }
86
87  private static getOrCreate<T>(
88    metrics: Metric<T>[],
89    className: string,
90    testName: string,
91    label: string
92  ): Metric<T> {
93    let metric: Metric<T> | null = Transforms.find(metrics, className, testName, label);
94    if (metric == null) {
95      const data: Record<string, ChartData<T>> = {};
96      metric = {
97        class: className,
98        benchmark: testName,
99        label: label,
100        data: data
101      }
102      metrics.push(metric);
103    }
104    return metric;
105  }
106
107  private static find<T>(
108    metrics: Metric<T>[],
109    className: string,
110    testName: string,
111    label: string
112  ): Metric<T> | null {
113    for (let i = 0; i < metrics.length; i += 1) {
114      const metric = metrics[i];
115      if (
116        metric.class === className &&
117        metric.benchmark === testName &&
118        metric.label === label
119      ) {
120        return metric;
121      }
122    }
123    return null;
124  }
125}
126