Skip to content

Commit 105b26c

Browse files
committed
prompt: support case-insensitive sorting
1 parent f145d30 commit 105b26c

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

cli/azd/pkg/prompt/prompter.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"os"
1212
"slices"
1313
"strconv"
14-
"strings"
1514

1615
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
1716
"github.com/MakeNowJust/heredoc/v2"
@@ -22,6 +21,7 @@ import (
2221
"github.com/azure/azure-dev/cli/azd/pkg/cloud"
2322
"github.com/azure/azure-dev/cli/azd/pkg/environment"
2423
"github.com/azure/azure-dev/cli/azd/pkg/input"
24+
"github.com/azure/azure-dev/cli/azd/pkg/stringutil"
2525
)
2626

2727
type LocationFilterPredicate func(loc account.Location) bool
@@ -157,7 +157,7 @@ func (p *DefaultPrompter) PromptResourceGroupFrom(
157157
}
158158

159159
slices.SortFunc(groups, func(a, b *azapi.Resource) int {
160-
return strings.Compare(a.Name, b.Name)
160+
return stringutil.CompareLower(a.Name, b.Name)
161161
})
162162

163163
canCreateNeResourceGroup := !options.DisableCreateNew
@@ -218,6 +218,10 @@ func (p *DefaultPrompter) getSubscriptionOptions(ctx context.Context) ([]string,
218218
return nil, nil, nil, fmt.Errorf("listing accounts: %w", err)
219219
}
220220

221+
slices.SortFunc(subscriptionInfos, func(a, b account.Subscription) int {
222+
return stringutil.CompareLower(a.Name, b.Name)
223+
})
224+
221225
// The default value is based on AZURE_SUBSCRIPTION_ID, falling back to whatever default subscription in
222226
// set in azd's config.
223227
defaultSubscriptionId := os.Getenv(environment.SubscriptionIdEnvVarName)

cli/azd/pkg/stringutil/stringutil.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package stringutil
5+
6+
import (
7+
"unicode"
8+
"unicode/utf8"
9+
)
10+
11+
// CompareLower returns an integer comparing two strings in a case-insensitive manner.
12+
// The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
13+
//
14+
// This comparison does not obey locale-specific rules.
15+
// To compare strings based on case-insensitive, locale-specific ordering, see [golang.org/x/text/collate].
16+
func CompareLower(a, b string) int {
17+
for {
18+
rb, nb := utf8.DecodeRuneInString(b)
19+
if nb == 0 {
20+
// len(a) > len(b), a > b.
21+
return 1
22+
}
23+
24+
ra, na := utf8.DecodeRuneInString(a)
25+
if na == 0 {
26+
// len(b) > len(a), b > a.
27+
return -1
28+
}
29+
30+
rb = unicode.ToLower(rb)
31+
ra = unicode.ToLower(ra)
32+
33+
if ra > rb {
34+
return 1
35+
} else if ra < rb {
36+
return -1
37+
}
38+
39+
// Trim slices to the next rune.
40+
a = a[na:]
41+
b = b[nb:]
42+
}
43+
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package stringutil
5+
6+
import (
7+
"slices"
8+
"testing"
9+
)
10+
11+
func TestCompareLowerSort(t *testing.T) {
12+
unordered := []string{
13+
"Zebra",
14+
"apple",
15+
"applesauce",
16+
"Banana",
17+
"CHERRY",
18+
"date",
19+
"",
20+
"Apple",
21+
"café",
22+
"cafe",
23+
"APPLE",
24+
}
25+
26+
expected := []string{
27+
"",
28+
"apple",
29+
"Apple",
30+
"APPLE",
31+
"applesauce",
32+
"Banana",
33+
"cafe",
34+
"café",
35+
"CHERRY",
36+
"date",
37+
"Zebra",
38+
}
39+
40+
slices.SortFunc(unordered, func(a, b string) int {
41+
return CompareLower(a, b)
42+
})
43+
44+
if !slices.Equal(unordered, expected) {
45+
t.Errorf("incorrect sort order:\ngot: %q\nwant: %q", unordered, expected)
46+
}
47+
}

0 commit comments

Comments
 (0)