From f1d333f0e6c97cabbc64e83899d7d16b60e878a5 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 20 Mar 2026 15:12:41 +0000 Subject: [PATCH] refactor: deduplicate utility helpers across the codebase (#23338) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audited exported helpers in `coderd/util/*`, `testutil`, `cryptorand`, and friends, then replaced duplicated implementations with canonical versions. - **fix: `maps.SortedKeys` generic signature** — value type was hardcoded to `any`, making it impossible to actually call. Added second type parameter `V any`. Added table-driven tests with `cmp.Diff`. - **refactor: replace ad-hoc ptr helpers with `ptr.Ref`** — removed `int64Ptr`, `stringPtr`, `boolPtr`, `i64ptr`, `strPtr`, `PtrInt32` across 6 files. - **refactor: replace local `sortedKeys`/`sortKeys` with `maps.SortedKeys`** — now that the signature is fixed, scripts can use it. - **refactor: replace hand-rolled `capitalize` with `strings.Capitalize`** — the typegen version was also not UTF-8 safe. > 🤖 This PR was created with the help of Coder Agents, and was reviewed by my human. 🧑‍💻 --- coderd/chatd/chatd.go | 15 +++--- .../chatd/chatprovider/chatprovider_test.go | 53 ++++++++----------- coderd/userauth_test.go | 9 ++-- coderd/util/maps/maps.go | 2 +- coderd/util/maps/maps_test.go | 44 +++++++++++++++ .../aibridgedserver/aibridgedserver_test.go | 33 ++++++------ provisioner/terraform/resources.go | 13 ++--- provisioner/terraform/resources_test.go | 25 ++++----- scripts/auditdocgen/main.go | 15 ++---- scripts/metricsdocgen/main.go | 12 +---- scripts/typegen/main.go | 9 ++-- 11 files changed, 116 insertions(+), 114 deletions(-) diff --git a/coderd/chatd/chatd.go b/coderd/chatd/chatd.go index 1265166c12..5e43ca3252 100644 --- a/coderd/chatd/chatd.go +++ b/coderd/chatd/chatd.go @@ -30,6 +30,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/pubsub" coderdpubsub "github.com/coder/coder/v2/coderd/pubsub" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/coderd/webpush" "github.com/coder/coder/v2/coderd/workspacestats" "github.com/coder/coder/v2/codersdk" @@ -2916,19 +2917,19 @@ func (p *Server) runChat( var usageForCost codersdk.ChatMessageUsage if hasUsage { if step.Usage.InputTokens != 0 { - usageForCost.InputTokens = int64Ptr(step.Usage.InputTokens) + usageForCost.InputTokens = ptr.Ref(step.Usage.InputTokens) } if step.Usage.OutputTokens != 0 { - usageForCost.OutputTokens = int64Ptr(step.Usage.OutputTokens) + usageForCost.OutputTokens = ptr.Ref(step.Usage.OutputTokens) } if step.Usage.ReasoningTokens != 0 { - usageForCost.ReasoningTokens = int64Ptr(step.Usage.ReasoningTokens) + usageForCost.ReasoningTokens = ptr.Ref(step.Usage.ReasoningTokens) } if step.Usage.CacheCreationTokens != 0 { - usageForCost.CacheCreationTokens = int64Ptr(step.Usage.CacheCreationTokens) + usageForCost.CacheCreationTokens = ptr.Ref(step.Usage.CacheCreationTokens) } if step.Usage.CacheReadTokens != 0 { - usageForCost.CacheReadTokens = int64Ptr(step.Usage.CacheReadTokens) + usageForCost.CacheReadTokens = ptr.Ref(step.Usage.CacheReadTokens) } } totalCostMicros := chatcost.CalculateTotalCostMicros(usageForCost, callConfig.Cost) @@ -3532,10 +3533,6 @@ func (p *Server) resolveModelConfig( return defaultConfig, nil } -func int64Ptr(value int64) *int64 { - return &value -} - func refreshChatWorkspaceSnapshot( ctx context.Context, chat database.Chat, diff --git a/coderd/chatd/chatprovider/chatprovider_test.go b/coderd/chatd/chatprovider/chatprovider_test.go index 8737be0ca7..88fe7a0fcb 100644 --- a/coderd/chatd/chatprovider/chatprovider_test.go +++ b/coderd/chatd/chatprovider/chatprovider_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/coder/coder/v2/coderd/chatd/chatprovider" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" ) @@ -25,37 +26,37 @@ func TestReasoningEffortFromChat(t *testing.T) { { name: "OpenAICaseInsensitive", provider: "openai", - input: stringPtr(" HIGH "), - want: stringPtr(string(fantasyopenai.ReasoningEffortHigh)), + input: ptr.Ref(" HIGH "), + want: ptr.Ref(string(fantasyopenai.ReasoningEffortHigh)), }, { name: "AnthropicEffort", provider: "anthropic", - input: stringPtr("max"), - want: stringPtr(string(fantasyanthropic.EffortMax)), + input: ptr.Ref("max"), + want: ptr.Ref(string(fantasyanthropic.EffortMax)), }, { name: "OpenRouterEffort", provider: "openrouter", - input: stringPtr("medium"), - want: stringPtr(string(fantasyopenrouter.ReasoningEffortMedium)), + input: ptr.Ref("medium"), + want: ptr.Ref(string(fantasyopenrouter.ReasoningEffortMedium)), }, { name: "VercelEffort", provider: "vercel", - input: stringPtr("xhigh"), - want: stringPtr(string(fantasyvercel.ReasoningEffortXHigh)), + input: ptr.Ref("xhigh"), + want: ptr.Ref(string(fantasyvercel.ReasoningEffortXHigh)), }, { name: "InvalidEffortReturnsNil", provider: "openai", - input: stringPtr("unknown"), + input: ptr.Ref("unknown"), want: nil, }, { name: "UnsupportedProviderReturnsNil", provider: "bedrock", - input: stringPtr("high"), + input: ptr.Ref("high"), want: nil, }, { @@ -83,7 +84,7 @@ func TestMergeMissingProviderOptions_OpenRouterNested(t *testing.T) { options := &codersdk.ChatModelProviderOptions{ OpenRouter: &codersdk.ChatModelOpenRouterProviderOptions{ Reasoning: &codersdk.ChatModelReasoningOptions{ - Enabled: boolPtr(true), + Enabled: ptr.Ref(true), }, Provider: &codersdk.ChatModelOpenRouterProvider{ Order: []string{"openai"}, @@ -93,21 +94,21 @@ func TestMergeMissingProviderOptions_OpenRouterNested(t *testing.T) { defaults := &codersdk.ChatModelProviderOptions{ OpenRouter: &codersdk.ChatModelOpenRouterProviderOptions{ Reasoning: &codersdk.ChatModelReasoningOptions{ - Enabled: boolPtr(false), - Exclude: boolPtr(true), - MaxTokens: int64Ptr(123), - Effort: stringPtr("high"), + Enabled: ptr.Ref(false), + Exclude: ptr.Ref(true), + MaxTokens: ptr.Ref[int64](123), + Effort: ptr.Ref("high"), }, - IncludeUsage: boolPtr(true), + IncludeUsage: ptr.Ref(true), Provider: &codersdk.ChatModelOpenRouterProvider{ Order: []string{"anthropic"}, - AllowFallbacks: boolPtr(true), - RequireParameters: boolPtr(false), - DataCollection: stringPtr("allow"), + AllowFallbacks: ptr.Ref(true), + RequireParameters: ptr.Ref(false), + DataCollection: ptr.Ref("allow"), Only: []string{"openai"}, Ignore: []string{"foo"}, Quantizations: []string{"int8"}, - Sort: stringPtr("latency"), + Sort: ptr.Ref("latency"), }, }, } @@ -136,15 +137,3 @@ func TestMergeMissingProviderOptions_OpenRouterNested(t *testing.T) { require.Equal(t, []string{"int8"}, options.OpenRouter.Provider.Quantizations) require.Equal(t, "latency", *options.OpenRouter.Provider.Sort) } - -func stringPtr(value string) *string { - return &value -} - -func boolPtr(value bool) *bool { - return &value -} - -func int64Ptr(value int64) *int64 { - return &value -} diff --git a/coderd/userauth_test.go b/coderd/userauth_test.go index b5df59ae82..0e9ffb7de0 100644 --- a/coderd/userauth_test.go +++ b/coderd/userauth_test.go @@ -43,6 +43,7 @@ import ( "github.com/coder/coder/v2/coderd/notifications" "github.com/coder/coder/v2/coderd/notifications/notificationstest" "github.com/coder/coder/v2/coderd/promoauth" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/cryptorand" "github.com/coder/coder/v2/testutil" @@ -405,7 +406,7 @@ func TestUserOAuth2Github(t *testing.T) { AuthenticatedUser: func(ctx context.Context, _ *http.Client) (*github.User, error) { return &github.User{ AvatarURL: github.String("/hello-world"), - ID: i64ptr(1234), + ID: ptr.Ref[int64](1234), Login: github.String("kyle"), Name: github.String("Kylium Carbonate"), }, nil @@ -473,7 +474,7 @@ func TestUserOAuth2Github(t *testing.T) { AuthenticatedUser: func(_ context.Context, _ *http.Client) (*github.User, error) { return &github.User{ AvatarURL: github.String("/hello-world"), - ID: i64ptr(1234), + ID: ptr.Ref[int64](1234), Login: github.String("kyle"), Name: github.String(" " + strings.Repeat("a", 129) + " "), }, nil @@ -2525,10 +2526,6 @@ func oauth2Callback(t *testing.T, client *codersdk.Client, opts ...func(*http.Re return res } -func i64ptr(i int64) *int64 { - return &i -} - func authCookieValue(cookies []*http.Cookie) string { for _, cookie := range cookies { if cookie.Name == codersdk.SessionTokenCookie { diff --git a/coderd/util/maps/maps.go b/coderd/util/maps/maps.go index 6a858bf3f7..0da24bd8bf 100644 --- a/coderd/util/maps/maps.go +++ b/coderd/util/maps/maps.go @@ -31,7 +31,7 @@ func Subset[T, U comparable](a, b map[T]U) bool { } // SortedKeys returns the keys of m in sorted order. -func SortedKeys[T constraints.Ordered](m map[T]any) (keys []T) { +func SortedKeys[K constraints.Ordered, V any](m map[K]V) (keys []K) { for k := range m { keys = append(keys, k) } diff --git a/coderd/util/maps/maps_test.go b/coderd/util/maps/maps_test.go index f8ad8ddbc4..ac2d2e1e82 100644 --- a/coderd/util/maps/maps_test.go +++ b/coderd/util/maps/maps_test.go @@ -4,9 +4,53 @@ import ( "strconv" "testing" + "github.com/google/go-cmp/cmp" + "github.com/coder/coder/v2/coderd/util/maps" ) +func TestSortedKeys(t *testing.T) { + t.Parallel() + + for idx, tc := range []struct { + name string + input map[string]int + expected []string + }{ + { + name: "SortsAlphabetically", + input: map[string]int{ + "banana": 1, + "apple": 2, + "cherry": 3, + }, + expected: []string{"apple", "banana", "cherry"}, + }, + { + name: "AlreadySorted", + input: map[string]int{ + "alpha": 1, + "mango": 2, + "zebra": 3, + }, + expected: []string{"alpha", "mango", "zebra"}, + }, + { + name: "EmptyMap", + input: map[string]int{}, + expected: nil, + }, + } { + t.Run("#"+strconv.Itoa(idx)+"_"+tc.name, func(t *testing.T) { + t.Parallel() + got := maps.SortedKeys(tc.input) + if diff := cmp.Diff(tc.expected, got); diff != "" { + t.Fatalf("unexpected result (-want +got):\n%s", diff) + } + }) + } +} + func TestSubset(t *testing.T) { t.Parallel() diff --git a/enterprise/aibridgedserver/aibridgedserver_test.go b/enterprise/aibridgedserver/aibridgedserver_test.go index c9f9e97b23..b0cea1ba67 100644 --- a/enterprise/aibridgedserver/aibridgedserver_test.go +++ b/enterprise/aibridgedserver/aibridgedserver_test.go @@ -33,6 +33,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/externalauth" codermcp "github.com/coder/coder/v2/coderd/mcp" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/cryptorand" "github.com/coder/coder/v2/enterprise/aibridged" @@ -421,7 +422,7 @@ func TestRecordInterception(t *testing.T) { Model: "claude-4-opus", Metadata: metadataProto, StartedAt: timestamppb.Now(), - ClientSessionId: strPtr("session-abc-123"), + ClientSessionId: ptr.Ref("session-abc-123"), }, setupMocks: func(t *testing.T, db *dbmock.MockStore, req *proto.RecordInterceptionRequest) { interceptionID, err := uuid.Parse(req.GetId()) @@ -459,7 +460,7 @@ func TestRecordInterception(t *testing.T) { Model: "claude-4-opus", Metadata: metadataProto, StartedAt: timestamppb.Now(), - ClientSessionId: strPtr(""), + ClientSessionId: ptr.Ref(""), }, setupMocks: func(t *testing.T, db *dbmock.MockStore, req *proto.RecordInterceptionRequest) { interceptionID, err := uuid.Parse(req.GetId()) @@ -546,7 +547,7 @@ func TestRecordInterception(t *testing.T) { Provider: "anthropic", Model: "claude-4-opus", StartedAt: timestamppb.Now(), - CorrelatingToolCallId: strPtr("call_abc"), + CorrelatingToolCallId: ptr.Ref("call_abc"), }, setupMocks: func(t *testing.T, db *dbmock.MockStore, req *proto.RecordInterceptionRequest) { selfID, err := uuid.Parse(req.GetId()) @@ -580,7 +581,7 @@ func TestRecordInterception(t *testing.T) { Provider: "anthropic", Model: "claude-4-opus", StartedAt: timestamppb.Now(), - CorrelatingToolCallId: strPtr("call_abc"), + CorrelatingToolCallId: ptr.Ref("call_abc"), }, setupMocks: func(t *testing.T, db *dbmock.MockStore, req *proto.RecordInterceptionRequest) { selfID, err := uuid.Parse(req.GetId()) @@ -609,7 +610,7 @@ func TestRecordInterception(t *testing.T) { Provider: "anthropic", Model: "claude-4-opus", StartedAt: timestamppb.Now(), - CorrelatingToolCallId: strPtr("call_abc"), + CorrelatingToolCallId: ptr.Ref("call_abc"), }, setupMocks: func(t *testing.T, db *dbmock.MockStore, req *proto.RecordInterceptionRequest) { selfID, err := uuid.Parse(req.GetId()) @@ -641,7 +642,7 @@ func TestRecordInterception(t *testing.T) { Provider: "anthropic", Model: "claude-4-opus", StartedAt: timestamppb.Now(), - CorrelatingToolCallId: strPtr("call_orphan"), + CorrelatingToolCallId: ptr.Ref("call_orphan"), }, setupMocks: func(t *testing.T, db *dbmock.MockStore, req *proto.RecordInterceptionRequest) { selfID, err := uuid.Parse(req.GetId()) @@ -901,11 +902,11 @@ func TestRecordToolUsage(t *testing.T) { InterceptionId: uuid.NewString(), MsgId: "msg_123", ToolCallId: "call_xyz", - ServerUrl: strPtr("https://api.example.com"), + ServerUrl: ptr.Ref("https://api.example.com"), Tool: "read_file", Input: `{"path": "/etc/hosts"}`, Injected: false, - InvocationError: strPtr("permission denied"), + InvocationError: ptr.Ref("permission denied"), Metadata: metadataProto, CreatedAt: timestamppb.Now(), }, @@ -1107,10 +1108,6 @@ func mustMarshalAny(t *testing.T, msg protobufproto.Message) *anypb.Any { return v } -func strPtr(s string) *string { - return &s -} - // logLine represents a parsed JSON log entry. type logLine struct { Msg string `json:"msg"` @@ -1192,8 +1189,8 @@ func TestStructuredLogging(t *testing.T) { Model: "claude-4-opus", Metadata: metadataProto, StartedAt: timestamppb.Now(), - CorrelatingToolCallId: strPtr(toolCallID), - ClientSessionId: strPtr(sessionID), + CorrelatingToolCallId: ptr.Ref(toolCallID), + ClientSessionId: ptr.Ref(sessionID), }) return err @@ -1344,11 +1341,11 @@ func TestStructuredLogging(t *testing.T) { _, err := srv.RecordToolUsage(ctx, &proto.RecordToolUsageRequest{ InterceptionId: intcID.String(), MsgId: "msg_123", - ServerUrl: strPtr("https://api.example.com"), + ServerUrl: ptr.Ref("https://api.example.com"), Tool: "read_file", Input: `{"path": "/etc/hosts"}`, Injected: true, - InvocationError: strPtr("permission denied"), + InvocationError: ptr.Ref("permission denied"), Metadata: metadataProto, CreatedAt: timestamppb.Now(), }) @@ -1487,7 +1484,7 @@ func TestInferredThreadsByToolCalls(t *testing.T) { Provider: "anthropic", Model: "claude-4-opus", StartedAt: timestamppb.Now(), - CorrelatingToolCallId: strPtr("call_a"), + CorrelatingToolCallId: ptr.Ref("call_a"), }) require.NoError(t, err) @@ -1515,7 +1512,7 @@ func TestInferredThreadsByToolCalls(t *testing.T) { Provider: "anthropic", Model: "claude-4-opus", StartedAt: timestamppb.Now(), - CorrelatingToolCallId: strPtr("call_b"), + CorrelatingToolCallId: ptr.Ref("call_b"), }) require.NoError(t, err) diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 649e3b4b9b..8c7c28a436 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -16,6 +16,7 @@ import ( "golang.org/x/xerrors" "cdr.dev/slog/v3" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/coderd/util/slice" stringutil "github.com/coder/coder/v2/coderd/util/strings" "github.com/coder/coder/v2/codersdk" @@ -889,10 +890,12 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s } if !param.Validation[0].MaxDisabled { - protoParam.ValidationMax = PtrInt32(param.Validation[0].Max) + // #nosec G115 - Safe conversion as the number is expected to be within int32 range + protoParam.ValidationMax = ptr.Ref(int32(param.Validation[0].Max)) } if !param.Validation[0].MinDisabled { - protoParam.ValidationMin = PtrInt32(param.Validation[0].Min) + // #nosec G115 - Safe conversion as the number is expected to be within int32 range + protoParam.ValidationMin = ptr.Ref(int32(param.Validation[0].Min)) } protoParam.ValidationMonotonic = param.Validation[0].Monotonic } @@ -1141,12 +1144,6 @@ func safeInt32Conversion(n int) int32 { return int32(n) } -func PtrInt32(number int) *int32 { - // #nosec G115 - Safe conversion as the number is expected to be within int32 range - n := int32(number) - return &n -} - // sortedResourcesByType collects all resources of the given type from the // label map and returns them sorted by address. This ensures deterministic // iteration order when processing resources that are stored in Go maps. diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index 4a3c517378..a14017e2dc 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -20,6 +20,7 @@ import ( "cdr.dev/slog/v3" "cdr.dev/slog/v3/sloggers/slogtest" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/cryptorand" "github.com/coder/coder/v2/provisioner/terraform" "github.com/coder/coder/v2/provisionersdk/proto" @@ -699,22 +700,22 @@ func TestConvertResources(t *testing.T) { Name: "number_example_max_zero", Type: "number", DefaultValue: "-2", - ValidationMin: terraform.PtrInt32(-3), - ValidationMax: terraform.PtrInt32(0), + ValidationMin: ptr.Ref(int32(-3)), + ValidationMax: ptr.Ref(int32(0)), FormType: proto.ParameterFormType_INPUT, }, { Name: "number_example_min_max", Type: "number", DefaultValue: "4", - ValidationMin: terraform.PtrInt32(3), - ValidationMax: terraform.PtrInt32(6), + ValidationMin: ptr.Ref(int32(3)), + ValidationMax: ptr.Ref(int32(6)), FormType: proto.ParameterFormType_INPUT, }, { Name: "number_example_min_zero", Type: "number", DefaultValue: "4", - ValidationMin: terraform.PtrInt32(0), - ValidationMax: terraform.PtrInt32(6), + ValidationMin: ptr.Ref(int32(0)), + ValidationMax: ptr.Ref(int32(6)), FormType: proto.ParameterFormType_INPUT, }, { Name: "Sample", @@ -783,34 +784,34 @@ func TestConvertResources(t *testing.T) { Type: "number", DefaultValue: "4", ValidationMin: nil, - ValidationMax: terraform.PtrInt32(6), + ValidationMax: ptr.Ref(int32(6)), FormType: proto.ParameterFormType_INPUT, }, { Name: "number_example_max_zero", Type: "number", DefaultValue: "-3", ValidationMin: nil, - ValidationMax: terraform.PtrInt32(0), + ValidationMax: ptr.Ref(int32(0)), FormType: proto.ParameterFormType_INPUT, }, { Name: "number_example_min", Type: "number", DefaultValue: "4", - ValidationMin: terraform.PtrInt32(3), + ValidationMin: ptr.Ref(int32(3)), ValidationMax: nil, FormType: proto.ParameterFormType_INPUT, }, { Name: "number_example_min_max", Type: "number", DefaultValue: "4", - ValidationMin: terraform.PtrInt32(3), - ValidationMax: terraform.PtrInt32(6), + ValidationMin: ptr.Ref(int32(3)), + ValidationMax: ptr.Ref(int32(6)), FormType: proto.ParameterFormType_INPUT, }, { Name: "number_example_min_zero", Type: "number", DefaultValue: "4", - ValidationMin: terraform.PtrInt32(0), + ValidationMin: ptr.Ref(int32(0)), ValidationMax: nil, FormType: proto.ParameterFormType_INPUT, }}, diff --git a/scripts/auditdocgen/main.go b/scripts/auditdocgen/main.go index 98748fb4c1..66c8f4384b 100644 --- a/scripts/auditdocgen/main.go +++ b/scripts/auditdocgen/main.go @@ -5,12 +5,12 @@ import ( "flag" "log" "os" - "sort" "strconv" "strings" "golang.org/x/xerrors" + "github.com/coder/coder/v2/coderd/util/maps" "github.com/coder/coder/v2/enterprise/audit" "github.com/coder/coder/v2/scripts/atomicwrite" ) @@ -96,7 +96,7 @@ func readAuditDoc() ([]byte, error) { // Writes a markdown table of audit log resources to a buffer func updateAuditDoc(doc []byte, auditableResourcesMap AuditableResourcesMap) ([]byte, error) { // We must sort the resources to ensure table ordering - sortedResourceNames := sortKeys(auditableResourcesMap) + sortedResourceNames := maps.SortedKeys(auditableResourcesMap) i := bytes.Index(doc, generatorPrefix) if i < 0 { @@ -135,7 +135,7 @@ func updateAuditDoc(doc []byte, auditableResourcesMap AuditableResourcesMap) ([] _, _ = buffer.WriteString("|" + readableResourceName + "
" + auditActionsString + "|" + "|") // We must sort the field names to ensure sub-table ordering - sortedFieldNames := sortKeys(auditableResourcesMap[resourceName]) + sortedFieldNames := maps.SortedKeys(auditableResourcesMap[resourceName]) for _, fieldName := range sortedFieldNames { isTracked := auditableResourcesMap[resourceName][fieldName] @@ -153,12 +153,3 @@ func updateAuditDoc(doc []byte, auditableResourcesMap AuditableResourcesMap) ([] func writeAuditDoc(doc []byte) error { return atomicwrite.File(auditDocFile, doc) } - -func sortKeys[T any](stringMap map[string]T) []string { - var keyNames []string - for key := range stringMap { - keyNames = append(keyNames, key) - } - sort.Strings(keyNames) - return keyNames -} diff --git a/scripts/metricsdocgen/main.go b/scripts/metricsdocgen/main.go index d320b60c6a..302320e25e 100644 --- a/scripts/metricsdocgen/main.go +++ b/scripts/metricsdocgen/main.go @@ -14,6 +14,7 @@ import ( "github.com/prometheus/common/expfmt" "golang.org/x/xerrors" + "github.com/coder/coder/v2/coderd/util/maps" "github.com/coder/coder/v2/scripts/atomicwrite" ) @@ -176,7 +177,7 @@ func updatePrometheusDoc(doc []byte, metricFamilies []*dto.MetricFamily) ([]byte } if len(labels) > 0 { - _, _ = buffer.WriteString(strings.Join(sortedKeys(labels), " ")) + _, _ = buffer.WriteString(strings.Join(maps.SortedKeys(labels), " ")) } _, _ = buffer.WriteString(" |\n") @@ -190,12 +191,3 @@ func updatePrometheusDoc(doc []byte, metricFamilies []*dto.MetricFamily) ([]byte func writePrometheusDoc(doc []byte) error { return atomicwrite.File(prometheusDocFile, doc) } - -func sortedKeys(m map[string]struct{}) []string { - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} diff --git a/scripts/typegen/main.go b/scripts/typegen/main.go index 51af0b3d18..462066fc78 100644 --- a/scripts/typegen/main.go +++ b/scripts/typegen/main.go @@ -20,6 +20,7 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/rbac/policy" + utilstrings "github.com/coder/coder/v2/coderd/util/strings" "github.com/coder/coder/v2/codersdk" ) @@ -131,15 +132,11 @@ func generateCountries() ([]byte, error) { func pascalCaseName[T ~string](name T) string { names := strings.Split(string(name), "_") for i := range names { - names[i] = capitalize(names[i]) + names[i] = utilstrings.Capitalize(names[i]) } return strings.Join(names, "") } -func capitalize(name string) string { - return strings.ToUpper(string(name[0])) + name[1:] -} - type Definition struct { policy.PermissionDefinition Type string @@ -226,7 +223,7 @@ func generateRbacObjects(templateSource string) ([]byte, error) { var errorList []error var x int tpl, err := template.New("object.gotmpl").Funcs(template.FuncMap{ - "capitalize": capitalize, + "capitalize": utilstrings.Capitalize, "pascalCaseName": pascalCaseName[string], "actionsList": func() []ActionDetails { return actionList
FieldTracked