master
go 111 lines 3.08 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package program
4
5 import (
6 "fmt"
7 "slices"
8 )
9
10 // Program is an immutable compiled chart-template snapshot consumed by chartengine.
11 //
12 // Immutability contract:
13 // - New(...) deep-copies all caller-provided slices/maps.
14 // - getters return copies, never internal backing storage.
15 // - all internal indexes are built once at construction time.
16 //
17 // This keeps runtime planning deterministic and race-free while templates are
18 // recompiled/reloaded in parallel runtime paths.
19 type Program struct {
20 version string
21 revision uint64
22
23 metrics map[string]struct{}
24 metricNames []string
25 charts []Chart
26 chartByTmpl map[string]int
27 chartOrderID []string
28 }
29
30 // New constructs an immutable Program snapshot from precompiled IR items.
31 func New(version string, revision uint64, metrics []string, charts []Chart) (*Program, error) {
32 if version == "" {
33 return nil, fmt.Errorf("program: version is required")
34 }
35
36 p := &Program{
37 version: version,
38 revision: revision,
39 metrics: make(map[string]struct{}, len(metrics)),
40 charts: make([]Chart, 0, len(charts)),
41 chartByTmpl: make(map[string]int, len(charts)),
42 }
43
44 for _, name := range metrics {
45 if name == "" {
46 return nil, fmt.Errorf("program: metric name cannot be empty")
47 }
48 if _, ok := p.metrics[name]; ok {
49 continue
50 }
51 p.metrics[name] = struct{}{}
52 p.metricNames = append(p.metricNames, name)
53 }
54 slices.Sort(p.metricNames)
55
56 for i, chart := range charts {
57 if err := validateChart(chart); err != nil {
58 return nil, fmt.Errorf("program: chart[%d]: %w", i, err)
59 }
60 if _, exists := p.chartByTmpl[chart.TemplateID]; exists {
61 return nil, fmt.Errorf("program: duplicate chart template_id %q", chart.TemplateID)
62 }
63
64 clone := chart.clone()
65 p.chartByTmpl[clone.TemplateID] = len(p.charts)
66 p.chartOrderID = append(p.chartOrderID, clone.TemplateID)
67 p.charts = append(p.charts, clone)
68 }
69
70 return p, nil
71 }
72
73 // Version returns the program schema/version marker.
74 func (p *Program) Version() string { return p.version }
75
76 // Revision returns a deterministic revision/hash value assigned by compiler.
77 func (p *Program) Revision() uint64 { return p.revision }
78
79 // MetricNames returns declared metric names in sorted deterministic order.
80 func (p *Program) MetricNames() []string {
81 return append([]string(nil), p.metricNames...)
82 }
83
84 // HasMetric reports whether a metric name is declared in program scope.
85 func (p *Program) HasMetric(name string) bool {
86 _, ok := p.metrics[name]
87 return ok
88 }
89
90 // ChartTemplateIDs returns chart template IDs in insertion order.
91 func (p *Program) ChartTemplateIDs() []string {
92 return append([]string(nil), p.chartOrderID...)
93 }
94
95 // Charts returns a deep copy of compiled charts in insertion order.
96 func (p *Program) Charts() []Chart {
97 out := make([]Chart, 0, len(p.charts))
98 for _, chart := range p.charts {
99 out = append(out, chart.clone())
100 }
101 return out
102 }
103
104 // Chart returns one chart by template ID.
105 func (p *Program) Chart(templateID string) (Chart, bool) {
106 idx, ok := p.chartByTmpl[templateID]
107 if !ok {
108 return Chart{}, false
109 }
110 return p.charts[idx].clone(), true
111 }