From fff6e5950d7f66012d13b699ef70b5190ed12f57 Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Fri, 27 Feb 2026 09:49:46 -0500 Subject: [PATCH] validation-gen/util: add ParseInt utility for canonical integer parsing Signed-off-by: Bryce Palmer --- .../cmd/validation-gen/util/util.go | 23 ++++++++ .../cmd/validation-gen/util/util_test.go | 52 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go index 4566699baff..208a5ea19b0 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go @@ -17,6 +17,9 @@ limitations under the License. package util import ( + "fmt" + "strconv" + "k8s.io/gengo/v2/parser/tags" "k8s.io/gengo/v2/types" ) @@ -114,3 +117,23 @@ func IsDirectComparable(t *types.Type) bool { } return false } + +// ParseInt strictly parses an int from a string input, +// ensuring that when converted back to a string, the resulting +// int and the input string have the exact same representation. +// This prevents scenarios where an input like `0100` parses +// as 100 and would be re-stringed as `100`. +func ParseInt(val string) (int, error) { + intVal, err := strconv.Atoi(val) + if err != nil { + return 0, fmt.Errorf("parsing %q as int: %w", val, err) + } + + strVal := strconv.Itoa(intVal) + if strVal != val { + err := fmt.Errorf("parsed int %d converted to a string value of %q which does not match the input string", intVal, strVal) + return 0, fmt.Errorf("parsing %q as int: %w", val, err) + } + + return intVal, nil +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go index 5ff3ae6cb8c..554e7bc3a80 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go @@ -452,3 +452,55 @@ func TestIsDirectComparable(t *testing.T) { } } } + +func TestParseInt(t *testing.T) { + type testcase struct { + name string + in string + expectedOut int + expectedError bool + } + + testcases := []testcase{ + { + name: "valid canonical integer string", + in: "100", + expectedOut: 100, + expectedError: false, + }, + { + name: "invalid canonical integer string, not an integer at all", + in: "notanint", + expectedOut: 0, + expectedError: true, + }, + { + name: "invalid canonical integer string, spurious leading zeros", + in: "00100", + expectedOut: 0, + expectedError: true, + }, + { + name: "invalid canonical integer string, octal value", + in: "0o123", + expectedOut: 0, + expectedError: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + out, err := ParseInt(tc.in) + switch { + case tc.expectedError && err == nil: + t.Error("expected an error but did not receive one") + case !tc.expectedError && err != nil: + t.Errorf("received an unexpected error: %v", err) + } + + if out != tc.expectedOut { + t.Errorf("expected an output value of %d but got %d", tc.expectedOut, out) + } + }) + } +}