From 7d754768b8d610015cdb016ecda54bbdad54fa6c Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 24 May 2026 14:02:57 +0300 Subject: fix(rpn): extract metric inference rules into data-driven maps (task kj) Replace hardcoded switch statements in resultMetricForMul and resultMetricForDiv with package-level maps (multiplicationInference, divisionInference). Adding new category pairs now only requires appending to the maps instead of editing control flow, satisfying OCP. --- internal/rpn/operations_metric.go | 42 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/internal/rpn/operations_metric.go b/internal/rpn/operations_metric.go index decaad8..64cce98 100644 --- a/internal/rpn/operations_metric.go +++ b/internal/rpn/operations_metric.go @@ -106,6 +106,15 @@ func convertFromBase(reg *MetricRegistry, baseVal float64, m *Metric, mode Prefi return baseVal / m.Factor(mode), nil } +// multiplicationInference maps ordered category pairs to the result base-unit name. +// Both orderings are stored so that map lookup replaces the switch for commutative mul. +var multiplicationInference = map[[2]Category]string{ + {DataRate, Time}: "bits", + {Time, DataRate}: "bits", + {Speed, Time}: "m", + {Time, Speed}: "m", +} + // resultMetricForMul computes the resulting metric for multiplication. func resultMetricForMul(reg *MetricRegistry, a, b *Metric) (*Metric, error) { if a == nil || a.Category == Universal { @@ -118,19 +127,17 @@ func resultMetricForMul(reg *MetricRegistry, a, b *Metric) (*Metric, error) { return a, nil } - // Cross-category inference - switch { - case a.Category == DataRate && b.Category == Time: - return baseMetric(reg, "bits") - case a.Category == Time && b.Category == DataRate: - return baseMetric(reg, "bits") - case a.Category == Speed && b.Category == Time: - return baseMetric(reg, "m") - case a.Category == Time && b.Category == Speed: - return baseMetric(reg, "m") - default: - return baseMetric(reg, "Cool") + if name, ok := multiplicationInference[[2]Category{a.Category, b.Category}]; ok { + return baseMetric(reg, name) } + return baseMetric(reg, "Cool") +} + +// divisionInference maps (dividend, divisor) category pairs to the result base-unit name. +// Unlike multiplication, division is not commutative so order matters. +var divisionInference = map[[2]Category]string{ + {DataSize, Time}: "bps", + {Distance, Time}: "mps", } // resultMetricForDiv computes the resulting metric for division. @@ -151,15 +158,10 @@ func resultMetricForDiv(reg *MetricRegistry, a, b *Metric) (*Metric, error) { return baseMetric(reg, "Cool") } - // Cross-category inference - switch { - case a.Category == DataSize && b.Category == Time: - return baseMetric(reg, "bps") - case a.Category == Distance && b.Category == Time: - return baseMetric(reg, "mps") - default: - return baseMetric(reg, "Cool") + if name, ok := divisionInference[[2]Category{a.Category, b.Category}]; ok { + return baseMetric(reg, name) } + return baseMetric(reg, "Cool") } // metricError returns a descriptive error for incompatible metric operations. -- cgit v1.2.3