From 83e0c24a731742b171d20afebf970c9d54b8543a Mon Sep 17 00:00:00 2001 From: TristonianJones Date: Mon, 17 Nov 2025 15:18:58 -0800 Subject: [PATCH] Fix the formatting directives to support what's documented in cel-spec --- ext/formatting_v2.go | 8 +++--- ext/formatting_v2_test.go | 54 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/ext/formatting_v2.go b/ext/formatting_v2.go index ca8efbc4e..6ac55b5d9 100644 --- a/ext/formatting_v2.go +++ b/ext/formatting_v2.go @@ -245,13 +245,13 @@ func (c *stringFormatterV2) Fixed(precision int) func(ref.Val) (string, error) { if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType) } - return fmt.Sprintf(fmtStr, argInt), nil + return fmt.Sprintf(fmtStr, float64(argInt)), nil case types.UintType: argUint, ok := arg.Value().(uint64) if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType) } - return fmt.Sprintf(fmtStr, argUint), nil + return fmt.Sprintf(fmtStr, float64(argUint)), nil case types.DoubleType: argDbl, ok := arg.Value().(float64) if !ok { @@ -283,13 +283,13 @@ func (c *stringFormatterV2) Scientific(precision int) func(ref.Val) (string, err if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType) } - return fmt.Sprintf(fmtStr, argInt), nil + return fmt.Sprintf(fmtStr, float64(argInt)), nil case types.UintType: argUint, ok := arg.Value().(uint64) if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType) } - return fmt.Sprintf(fmtStr, argUint), nil + return fmt.Sprintf(fmtStr, float64(argUint)), nil case types.DoubleType: argDbl, ok := arg.Value().(float64) if !ok { diff --git a/ext/formatting_v2_test.go b/ext/formatting_v2_test.go index d574e62de..46aeb688e 100644 --- a/ext/formatting_v2_test.go +++ b/ext/formatting_v2_test.go @@ -116,7 +116,7 @@ func TestStringFormatV2(t *testing.T) { formatArgs: `"percent"`, expectedOutput: "%escaped percent%", expectedRuntimeCost: 12, - expectedEstimatedCost: checker.CostEstimate{Min: 12, Max: 12}, + expectedEstimatedCost: checker.FixedCostEstimate(12), }, { name: "fixed point formatting clause", @@ -124,7 +124,15 @@ func TestStringFormatV2(t *testing.T) { formatArgs: "1.2345", expectedOutput: "1.234", expectedRuntimeCost: 11, - expectedEstimatedCost: checker.CostEstimate{Min: 11, Max: 11}, + expectedEstimatedCost: checker.FixedCostEstimate(11), + }, + { + name: "fixed point formatting clause with int", + format: "%.3f + %.2f", + formatArgs: "1, 1u", + expectedOutput: "1.000 + 1.00", + expectedRuntimeCost: 12, + expectedEstimatedCost: checker.FixedCostEstimate(12), }, { name: "binary formatting clause", @@ -262,6 +270,22 @@ func TestStringFormatV2(t *testing.T) { expectedRuntimeCost: 11, expectedEstimatedCost: checker.CostEstimate{Min: 11, Max: 11}, }, + { + name: "scientific notation formatting clause with uint", + format: "%.4e", + formatArgs: "1052u", + expectedOutput: "1.0520e+03", + expectedRuntimeCost: 11, + expectedEstimatedCost: checker.CostEstimate{Min: 11, Max: 11}, + }, + { + name: "scientific notation formatting clause with int", + format: "%.5e", + formatArgs: "1052", + expectedOutput: "1.05200e+03", + expectedRuntimeCost: 11, + expectedEstimatedCost: checker.CostEstimate{Min: 11, Max: 11}, + }, { name: "default precision for fixed-point clause", format: "%f", @@ -554,6 +578,19 @@ func TestStringFormatV2(t *testing.T) { expectedRuntimeCost: 13, expectedEstimatedCost: checker.CostEstimate{Min: 13, Max: 13}, }, + { + name: "dyntype NaN/infinity support for decimal", + format: "NaN: %d, infinity: %d, -infinity: %d", + formatArgs: `dynNaN, dynInf, dynNegInf`, + dynArgs: map[string]any{ + "dynNaN": math.NaN(), + "dynInf": math.Inf(1), + "dynNegInf": math.Inf(-1), + }, + expectedOutput: "NaN: NaN, infinity: Infinity, -infinity: -Infinity", + expectedRuntimeCost: 17, + expectedEstimatedCost: checker.FixedCostEstimate(17), + }, { name: "dyntype NaN/infinity support for fixed-point", format: "NaN: %f, infinity: %f", @@ -566,6 +603,19 @@ func TestStringFormatV2(t *testing.T) { expectedRuntimeCost: 15, expectedEstimatedCost: checker.CostEstimate{Min: 15, Max: 15}, }, + { + name: "dyntype NaN/infinity support for scientific", + format: "NaN: %e, infinity: %e, -infinity: %e", + formatArgs: `dynNaN, dynInf, dynNegInf`, + dynArgs: map[string]any{ + "dynNaN": math.NaN(), + "dynInf": math.Inf(1), + "dynNegInf": math.Inf(-1), + }, + expectedOutput: "NaN: NaN, infinity: Infinity, -infinity: -Infinity", + expectedRuntimeCost: 17, + expectedEstimatedCost: checker.FixedCostEstimate(17), + }, { name: "dyntype support for timestamp", format: "dyntype timestamp: %s",