From 14064c919e4e6cceded722644de14c23168f063c Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Tue, 2 Jun 2026 14:31:54 +0200 Subject: [PATCH] fix(obiutils): correctly unwrap interface values in min/max Introduces an `unwrapInterface` reflection helper to dereference `interface{}`-wrapped values before type validation. Updates slice and map iteration loops in min/max functions to apply this helper, ensuring `isOrderedKind` accurately identifies underlying concrete types instead of incorrectly rejecting `reflect.Interface` elements. --- pkg/obiutils/minmax.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/pkg/obiutils/minmax.go b/pkg/obiutils/minmax.go index 1312e06..66ef179 100644 --- a/pkg/obiutils/minmax.go +++ b/pkg/obiutils/minmax.go @@ -309,7 +309,7 @@ func saturatingSubValues(a, b reflect.Value) (reflect.Value, error) { func maxFromIterable(v reflect.Value) (interface{}, error) { var best reflect.Value for i := 0; i < v.Len(); i++ { - elem := v.Index(i) + elem := unwrapInterface(v.Index(i)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -324,7 +324,7 @@ func maxFromIterable(v reflect.Value) (interface{}, error) { func minFromIterable(v reflect.Value) (interface{}, error) { var minVal reflect.Value for i := 0; i < v.Len(); i++ { - elem := v.Index(i) + elem := unwrapInterface(v.Index(i)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -339,7 +339,7 @@ func filterMinFromIterable(v reflect.Value, minimum interface{}) (interface{}, e minVal := reflect.ValueOf(minimum) result := reflect.MakeSlice(v.Type(), 0, v.Len()) for i := 0; i < v.Len(); i++ { - elem := v.Index(i) + elem := unwrapInterface(v.Index(i)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -354,7 +354,7 @@ func filterMaxFromIterable(v reflect.Value, maximum interface{}) (interface{}, e maxVal := reflect.ValueOf(maximum) result := reflect.MakeSlice(v.Type(), 0, v.Len()) for i := 0; i < v.Len(); i++ { - elem := v.Index(i) + elem := unwrapInterface(v.Index(i)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -369,7 +369,7 @@ func filterMinFromMap(v reflect.Value, minimum interface{}) (interface{}, error) minVal := reflect.ValueOf(minimum) result := reflect.MakeMap(v.Type()) for _, key := range v.MapKeys() { - elem := v.MapIndex(key) + elem := unwrapInterface(v.MapIndex(key)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -384,7 +384,7 @@ func filterMaxFromMap(v reflect.Value, maximum interface{}) (interface{}, error) maxVal := reflect.ValueOf(maximum) result := reflect.MakeMap(v.Type()) for _, key := range v.MapKeys() { - elem := v.MapIndex(key) + elem := unwrapInterface(v.MapIndex(key)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -400,7 +400,7 @@ func maxFromMap(v reflect.Value) (interface{}, error) { var best reflect.Value first := true for _, key := range v.MapKeys() { - elem := v.MapIndex(key) + elem := unwrapInterface(v.MapIndex(key)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -417,7 +417,7 @@ func minFromMap(v reflect.Value) (interface{}, error) { var minVal reflect.Value first := true for _, key := range v.MapKeys() { - elem := v.MapIndex(key) + elem := unwrapInterface(v.MapIndex(key)) if !isOrderedKind(elem.Kind()) { return nil, fmt.Errorf("unsupported element type: %s", elem.Kind()) } @@ -440,6 +440,16 @@ func isNumericKind(k reflect.Kind) bool { } } +// unwrapInterface returns v.Elem() when v holds an interface value, otherwise v unchanged. +// This is necessary when iterating map[string]interface{} or []interface{} via reflection: +// the element Kind is reflect.Interface, not the underlying concrete type. +func unwrapInterface(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + return v.Elem() + } + return v +} + // isOrderedKind reports whether k supports comparison ordering. func isOrderedKind(k reflect.Kind) bool { switch k {