From 65c31a6f9d01e7c74f20ed37a3a988667db344b4 Mon Sep 17 00:00:00 2001 From: youdie006 Date: Fri, 4 Sep 2026 12:22:30 +0900 Subject: [PATCH] fix(casing): stop transforming an index after its part is removed Join decrements the outer index when a transform empties a part, but the inner transform loop kept running and then indexed the shifted slice one place too low. With a leading part removed this indexes -1 and panics; elsewhere it applies a later transform twice. --- casing/casing.go | 3 ++- casing/casing_test.go | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/casing/casing.go b/casing/casing.go index da7784c2..a4568ac5 100644 --- a/casing/casing.go +++ b/casing/casing.go @@ -214,9 +214,10 @@ func Join(parts []string, sep string, transform ...TransformFunc) string { parts[i] = t(parts[i]) if parts[i] == "" { - // Transformer completely removed this part. + // Removing the part shifts the rest down, so stop transforming this index. parts = append(parts[:i], parts[i+1:]...) i-- + break } } } diff --git a/casing/casing_test.go b/casing/casing_test.go index 101fb55d..73a16609 100644 --- a/casing/casing_test.go +++ b/casing/casing_test.go @@ -89,6 +89,23 @@ func TestRemovePart(t *testing.T) { })) } +func TestRemovePartWithMultipleTransforms(t *testing.T) { + drop := func(part string) string { + if part == "and" { + return "" + } + + return part + } + + // Camel always appends strings.Title, so a removing transform in first + // position leaves two transforms for the shifted index. + assert.Equal(t, "OneTwo", casing.Camel("and-one-two", drop)) + + suffix := func(part string) string { return part + "x" } + assert.Equal(t, "onex_twox", casing.Join([]string{"one", "and", "two"}, "_", drop, suffix)) +} + func TestRightAlign(t *testing.T) { assert.Equal(t, "stream_1080p", casing.Snake("Stream1080P"))