From 1e0de8f5597f3b37b9ae2ad8a2f8f2d2b40bde90 Mon Sep 17 00:00:00 2001 From: Ben Schumacher Date: Thu, 4 Apr 2024 13:44:03 +0200 Subject: [PATCH] [MM-57356] Make use of go1.21 features (#26620) --- server/channels/api4/user.go | 4 +- server/channels/api4/user_local.go | 4 +- server/channels/app/channel.go | 6 +- server/channels/app/notification_test.go | 8 +- .../app/platform/shared_channel_notifier.go | 4 +- server/channels/app/platform/web_conn.go | 4 +- server/channels/app/plugin_signature.go | 4 +- server/channels/app/role.go | 6 +- server/channels/app/role_test.go | 10 +- server/channels/app/session_test.go | 12 +- server/channels/app/user_agent.go | 8 - server/channels/app/web_broadcast_hooks.go | 6 +- server/channels/store/searchtest/testlib.go | 7 +- .../store/sqlstore/channel_bookmark_store.go | 6 +- .../channels/store/storetest/group_store.go | 6 +- server/channels/utils/api.go | 4 +- server/channels/utils/utils.go | 34 +- server/cmd/mmctl/commands/sampledata.go | 5 +- server/public/model/command_autocomplete.go | 5 +- server/public/model/report.go | 5 +- server/public/model/websocket_message.go | 11 +- server/public/pluginapi/kv_memory.go | 5 +- server/public/utils/utils.go | 14 - server/public/utils/utils_test.go | 388 ------------------ 24 files changed, 59 insertions(+), 507 deletions(-) delete mode 100644 server/public/utils/utils.go delete mode 100644 server/public/utils/utils_test.go diff --git a/server/channels/api4/user.go b/server/channels/api4/user.go index 0edaa7cbd0..4a9b0acb73 100644 --- a/server/channels/api4/user.go +++ b/server/channels/api4/user.go @@ -8,13 +8,13 @@ import ( "fmt" "io" "net/http" + "slices" "strconv" "strings" "time" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/mlog" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/app" "github.com/mattermost/mattermost/server/v8/channels/audit" @@ -726,7 +726,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) { c.SetInvalidParam("role") return } - roleValid := pUtils.Contains(roleNamesAll, role) + roleValid := slices.Contains(roleNamesAll, role) if !roleValid { c.SetInvalidParam("role") return diff --git a/server/channels/api4/user_local.go b/server/channels/api4/user_local.go index d56911ab36..7fbcd90839 100644 --- a/server/channels/api4/user_local.go +++ b/server/channels/api4/user_local.go @@ -6,12 +6,12 @@ package api4 import ( "encoding/json" "net/http" + "slices" "strconv" "strings" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/mlog" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/audit" "github.com/mattermost/mattermost/server/v8/channels/store" @@ -87,7 +87,7 @@ func localGetUsers(c *Context, w http.ResponseWriter, r *http.Request) { c.SetInvalidParam("role") return } - roleValid := pUtils.Contains(roleNamesAll, role) + roleValid := slices.Contains(roleNamesAll, role) if !roleValid { c.SetInvalidParam("role") return diff --git a/server/channels/app/channel.go b/server/channels/app/channel.go index cba64fa4fc..f5f2d5331e 100644 --- a/server/channels/app/channel.go +++ b/server/channels/app/channel.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "net/http" + "slices" "strings" "github.com/mattermost/mattermost/server/v8/channels/utils" @@ -18,7 +19,6 @@ import ( "github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/request" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store/sqlstore" ) @@ -1036,7 +1036,7 @@ func (a *App) PatchChannelModerationsForChannel(c request.CTX, channel *model.Ch for _, channelModerationPatch := range channelModerationsPatch { permissionModified := *channelModerationPatch.Name - if channelModerationPatch.Roles.Guests != nil && pUtils.Contains(model.ChannelModeratedPermissionsChangedByPatch(guestRole, guestRolePatch), permissionModified) { + if channelModerationPatch.Roles.Guests != nil && slices.Contains(model.ChannelModeratedPermissionsChangedByPatch(guestRole, guestRolePatch), permissionModified) { if *channelModerationPatch.Roles.Guests { c.Logger().Info("Permission enabled for guests.", mlog.String("permission", permissionModified), mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name)) } else { @@ -1044,7 +1044,7 @@ func (a *App) PatchChannelModerationsForChannel(c request.CTX, channel *model.Ch } } - if channelModerationPatch.Roles.Members != nil && pUtils.Contains(model.ChannelModeratedPermissionsChangedByPatch(memberRole, memberRolePatch), permissionModified) { + if channelModerationPatch.Roles.Members != nil && slices.Contains(model.ChannelModeratedPermissionsChangedByPatch(memberRole, memberRolePatch), permissionModified) { if *channelModerationPatch.Roles.Members { c.Logger().Info("Permission enabled for members.", mlog.String("permission", permissionModified), mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name)) } else { diff --git a/server/channels/app/notification_test.go b/server/channels/app/notification_test.go index 424cac692f..0611356d30 100644 --- a/server/channels/app/notification_test.go +++ b/server/channels/app/notification_test.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "slices" "testing" "time" @@ -17,7 +18,6 @@ import ( "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/i18n" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/app/platform" "github.com/mattermost/mattermost/server/v8/channels/store" @@ -56,7 +56,7 @@ func TestSendNotifications(t *testing.T) { mentions, err := th.App.SendNotifications(th.Context, post1, th.BasicTeam, th.BasicChannel, th.BasicUser, nil, true) require.NoError(t, err) require.NotNil(t, mentions) - require.True(t, pUtils.Contains(mentions, th.BasicUser2.Id), "mentions", mentions) + require.True(t, slices.Contains(mentions, th.BasicUser2.Id), "mentions", mentions) }) t.Run("license is required for group mention", func(t *testing.T) { @@ -197,7 +197,7 @@ func TestSendNotifications(t *testing.T) { } mentions, err := th.App.SendNotifications(th.Context, childPost, th.BasicTeam, th.BasicChannel, th.BasicUser2, &postList, true) require.NoError(t, err) - require.False(t, pUtils.Contains(mentions, user.Id)) + require.False(t, slices.Contains(mentions, user.Id)) } var appErr *model.AppError @@ -2831,7 +2831,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { } mentions, err := th.App.SendNotifications(th.Context, childPost, th.BasicTeam, th.BasicChannel, th.BasicUser2, &postList, true) require.NoError(t, err) - assert.False(t, pUtils.Contains(mentions, user.Id)) + assert.False(t, slices.Contains(mentions, user.Id)) membership, err := th.App.GetThreadMembershipForUser(user.Id, rootPost.Id) assert.Error(t, err) diff --git a/server/channels/app/platform/shared_channel_notifier.go b/server/channels/app/platform/shared_channel_notifier.go index e93b410ff0..459d815b97 100644 --- a/server/channels/app/platform/shared_channel_notifier.go +++ b/server/channels/app/platform/shared_channel_notifier.go @@ -6,12 +6,12 @@ package platform import ( "context" "fmt" + "slices" "github.com/pkg/errors" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/mlog" - "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/platform/services/sharedchannel" ) @@ -59,7 +59,7 @@ func (ps *PlatformService) SharedChannelSyncHandler(event *model.WebSocketEvent) func isEligibleForEvents(syncService SharedChannelServiceIFace, event *model.WebSocketEvent, events []model.WebsocketEventType) bool { return syncServiceEnabled(syncService) && eventHasChannel(event) && - utils.Contains(events, event.EventType()) + slices.Contains(events, event.EventType()) } func eventHasChannel(event *model.WebSocketEvent) bool { diff --git a/server/channels/app/platform/web_conn.go b/server/channels/app/platform/web_conn.go index 663bee42b1..99f5ca82b3 100644 --- a/server/channels/app/platform/web_conn.go +++ b/server/channels/app/platform/web_conn.go @@ -11,6 +11,7 @@ import ( "fmt" "net" "net/http" + "slices" "strconv" "strings" "sync" @@ -25,7 +26,6 @@ import ( "github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/request" - "github.com/mattermost/mattermost/server/public/utils" ) const ( @@ -877,7 +877,7 @@ func (wc *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool { // For typing events, we don't send them to users who don't have // that channel or thread opened. if wc.Platform.Config().FeatureFlags.WebSocketEventScope && - utils.Contains([]model.WebsocketEventType{ + slices.Contains([]model.WebsocketEventType{ model.WebsocketEventTyping, model.WebsocketEventReactionAdded, model.WebsocketEventReactionRemoved, diff --git a/server/channels/app/plugin_signature.go b/server/channels/app/plugin_signature.go index e976f907d5..9c1549fca6 100644 --- a/server/channels/app/plugin_signature.go +++ b/server/channels/app/plugin_signature.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "path/filepath" + "slices" "github.com/pkg/errors" "golang.org/x/crypto/openpgp" //nolint:staticcheck @@ -15,7 +16,6 @@ import ( "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/mlog" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/utils" ) @@ -48,7 +48,7 @@ func (a *App) AddPublicKey(name string, key io.Reader) *model.AppError { } a.UpdateConfig(func(cfg *model.Config) { - if !pUtils.Contains(cfg.PluginSettings.SignaturePublicKeyFiles, name) { + if !slices.Contains(cfg.PluginSettings.SignaturePublicKeyFiles, name) { cfg.PluginSettings.SignaturePublicKeyFiles = append(cfg.PluginSettings.SignaturePublicKeyFiles, name) } }) diff --git a/server/channels/app/role.go b/server/channels/app/role.go index 82a87fbc73..86eb9e4391 100644 --- a/server/channels/app/role.go +++ b/server/channels/app/role.go @@ -9,10 +9,10 @@ import ( "errors" "net/http" "reflect" + "slices" "strings" "github.com/mattermost/mattermost/server/public/model" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/utils" @@ -189,13 +189,13 @@ func (a *App) UpdateRole(role *model.Role) (*model.Role, *model.AppError) { builtInRolesMinusChannelRoles := append(utils.RemoveStringsFromSlice(model.BuiltInSchemeManagedRoleIDs, builtInChannelRoles...), model.NewSystemRoleIDs...) - if pUtils.Contains(builtInRolesMinusChannelRoles, savedRole.Name) { + if slices.Contains(builtInRolesMinusChannelRoles, savedRole.Name) { return savedRole, nil } var roleRetrievalFunc func() ([]*model.Role, *model.AppError) - if pUtils.Contains(builtInChannelRoles, savedRole.Name) { + if slices.Contains(builtInChannelRoles, savedRole.Name) { roleRetrievalFunc = func() ([]*model.Role, *model.AppError) { roles, nErr := a.Srv().Store().Role().AllChannelSchemeRoles() if nErr != nil { diff --git a/server/channels/app/role_test.go b/server/channels/app/role_test.go index 100de83a4f..f6fd63485e 100644 --- a/server/channels/app/role_test.go +++ b/server/channels/app/role_test.go @@ -8,6 +8,7 @@ import ( "encoding/csv" "io" "os" + "slices" "strconv" "strings" "testing" @@ -15,7 +16,6 @@ import ( "github.com/stretchr/testify/require" "github.com/mattermost/mattermost/server/public/model" - pUtils "github.com/mattermost/mattermost/server/public/utils" ) type permissionInheritanceTestData struct { @@ -37,7 +37,7 @@ func TestGetRolesByNames(t *testing.T) { require.NotNil(t, actualRole) require.Equal(t, testData.channelRole.Name, actualRole.Name) - require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id)) + require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id)) }) } @@ -47,7 +47,7 @@ func TestGetRoleByName(t *testing.T) { require.Nil(t, err) require.NotNil(t, actualRole) require.Equal(t, testData.channelRole.Name, actualRole.Name) - require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) + require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) }) } @@ -57,7 +57,7 @@ func TestGetRoleByID(t *testing.T) { require.Nil(t, err) require.NotNil(t, actualRole) require.Equal(t, testData.channelRole.Id, actualRole.Id) - require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) + require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) }) } @@ -69,7 +69,7 @@ func TestGetAllRoles(t *testing.T) { if actualRole.Id == testData.channelRole.Id { require.NotNil(t, actualRole) require.Equal(t, testData.channelRole.Id, actualRole.Id) - require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) + require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) } } }) diff --git a/server/channels/app/session_test.go b/server/channels/app/session_test.go index b53fa70f27..0556a16cc4 100644 --- a/server/channels/app/session_test.go +++ b/server/channels/app/session_test.go @@ -8,6 +8,7 @@ import ( "net/http" "net/http/httptest" "os" + "slices" "testing" "time" @@ -419,7 +420,7 @@ func TestSessionsLimit(t *testing.T) { require.Equal(t, maxSessionsLimit, len(gotSessions), "should have maxSessionsLimit number of sessions") // Ensure we are retrieving the same sessions. - reverse(gotSessions) + slices.Reverse(gotSessions) for i, sess := range gotSessions { require.Equal(t, sessions[i].Id, sess.Id) } @@ -440,15 +441,8 @@ func TestSessionsLimit(t *testing.T) { require.Equal(t, maxSessionsLimit, len(gotSessions), "should have maxSessionsLimit number of sessions") // Ensure the the oldest sessions were removed first. - reverse(gotSessions) + slices.Reverse(gotSessions) for i, sess := range gotSessions { require.Equal(t, sessions[i].Id, sess.Id) } } - -// reverse can be replaced by the slices version when we move to 1.21+ -func reverse[S ~[]E, E any](s S) { - for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } -} diff --git a/server/channels/app/user_agent.go b/server/channels/app/user_agent.go index c9918318e9..37a08cb607 100644 --- a/server/channels/app/user_agent.go +++ b/server/channels/app/user_agent.go @@ -159,11 +159,3 @@ func getBrowserName(ua *uasurfer.UserAgent, userAgentString string) string { return browserNames[uasurfer.BrowserUnknown] } - -// min should be replaced by to go 1.21 built-in generic function, see MM-57356. -func min(a, b int) int { - if a < b { - return a - } - return b -} diff --git a/server/channels/app/web_broadcast_hooks.go b/server/channels/app/web_broadcast_hooks.go index f17ee9d740..46c4e1c8d3 100644 --- a/server/channels/app/web_broadcast_hooks.go +++ b/server/channels/app/web_broadcast_hooks.go @@ -6,9 +6,9 @@ package app import ( "encoding/json" "fmt" + "slices" "github.com/mattermost/mattermost/server/public/model" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/app/platform" "github.com/pkg/errors" ) @@ -33,7 +33,7 @@ func (h *addMentionsBroadcastHook) Process(msg *platform.HookedWebSocketEvent, w return errors.Wrap(err, "Invalid mentions value passed to addMentionsBroadcastHook") } - if len(mentions) > 0 && pUtils.Contains[string](mentions, webConn.UserId) { + if len(mentions) > 0 && slices.Contains(mentions, webConn.UserId) { // Note that the client expects this field to be stringified msg.Add("mentions", model.ArrayToJSON([]string{webConn.UserId})) } @@ -55,7 +55,7 @@ func (h *addFollowersBroadcastHook) Process(msg *platform.HookedWebSocketEvent, return errors.Wrap(err, "Invalid followers value passed to addFollowersBroadcastHook") } - if len(followers) > 0 && pUtils.Contains[string](followers, webConn.UserId) { + if len(followers) > 0 && slices.Contains(followers, webConn.UserId) { // Note that the client expects this field to be stringified msg.Add("followers", model.ArrayToJSON([]string{webConn.UserId})) } diff --git a/server/channels/store/searchtest/testlib.go b/server/channels/store/searchtest/testlib.go index 9e6042df85..bab2dfbf37 100644 --- a/server/channels/store/searchtest/testlib.go +++ b/server/channels/store/searchtest/testlib.go @@ -4,10 +4,9 @@ package searchtest import ( + "slices" "testing" - pUtils "github.com/mattermost/mattermost/server/public/utils" - "github.com/mattermost/mattermost/server/v8/channels/store" ) @@ -36,12 +35,12 @@ type searchTest struct { func filterTestsByTag(tests []searchTest, tags ...string) []searchTest { filteredTests := []searchTest{} for _, test := range tests { - if pUtils.Contains(test.Tags, EngineAll) { + if slices.Contains(test.Tags, EngineAll) { filteredTests = append(filteredTests, test) continue } for _, tag := range tags { - if pUtils.Contains(test.Tags, tag) { + if slices.Contains(test.Tags, tag) { filteredTests = append(filteredTests, test) break } diff --git a/server/channels/store/sqlstore/channel_bookmark_store.go b/server/channels/store/sqlstore/channel_bookmark_store.go index ede8885155..47c9126871 100644 --- a/server/channels/store/sqlstore/channel_bookmark_store.go +++ b/server/channels/store/sqlstore/channel_bookmark_store.go @@ -4,15 +4,15 @@ package sqlstore import ( + "slices" "strconv" sq "github.com/mattermost/squirrel" + "github.com/pkg/errors" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/utils" - - "github.com/pkg/errors" ) type SqlChannelBookmarkStore struct { @@ -262,7 +262,7 @@ func (s *SqlChannelBookmarkStore) UpdateSortOrder(bookmarkId, channelId string, } bookmarks = utils.RemoveElementFromSliceAtIndex(bookmarks, currentIndex) - bookmarks = utils.InsertElementToSliceAtIndex(bookmarks, current, int(newIndex)) + bookmarks = slices.Insert(bookmarks, int(newIndex), current) caseStmt := sq.Case() query := s.getQueryBuilder(). Update("ChannelBookmarks") diff --git a/server/channels/store/storetest/group_store.go b/server/channels/store/storetest/group_store.go index aa465286f5..b92fb7c232 100644 --- a/server/channels/store/storetest/group_store.go +++ b/server/channels/store/storetest/group_store.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "math" + "slices" "sort" "strings" "testing" @@ -17,7 +18,6 @@ import ( "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/request" - pUtils "github.com/mattermost/mattermost/server/public/utils" "github.com/mattermost/mattermost/server/v8/channels/store" ) @@ -4826,7 +4826,7 @@ func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.St require.GreaterOrEqual(t, len(members), 4) // sanity check for team membership for _, member := range members { - if pUtils.Contains(tt.inUserIDs, member.UserId) { + if slices.Contains(tt.inUserIDs, member.UserId) { require.True(t, member.SchemeAdmin) } else { require.False(t, member.SchemeAdmin) @@ -4936,7 +4936,7 @@ func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store require.GreaterOrEqual(t, len(members), 4) // sanity check for channel membership for _, member := range members { - if pUtils.Contains(tt.inUserIDs, member.UserId) { + if slices.Contains(tt.inUserIDs, member.UserId) { require.True(t, member.SchemeAdmin) } else { require.False(t, member.SchemeAdmin) diff --git a/server/channels/utils/api.go b/server/channels/utils/api.go index ca865ab61c..f603bd9879 100644 --- a/server/channels/utils/api.go +++ b/server/channels/utils/api.go @@ -12,11 +12,11 @@ import ( "net/http" "net/url" "path" + "slices" "strings" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/i18n" - "github.com/mattermost/mattermost/server/public/utils" ) func CheckOrigin(r *http.Request, allowedOrigins string) bool { @@ -114,7 +114,7 @@ func RenderMobileAuthComplete(w http.ResponseWriter, redirectURL string) { func RenderMobileError(config *model.Config, w http.ResponseWriter, err *model.AppError, redirectURL string) { var link = template.HTMLEscapeString(redirectURL) u, redirectErr := url.Parse(redirectURL) - if redirectErr != nil || !utils.Contains(config.NativeAppSettings.AppCustomURLSchemes, u.Scheme) { + if redirectErr != nil || !slices.Contains(config.NativeAppSettings.AppCustomURLSchemes, u.Scheme) { link = *config.ServiceSettings.SiteURL } RenderMobileMessage(w, ` diff --git a/server/channels/utils/utils.go b/server/channels/utils/utils.go index aa60c92798..d99b260eb5 100644 --- a/server/channels/utils/utils.go +++ b/server/channels/utils/utils.go @@ -9,12 +9,12 @@ import ( "net" "net/http" "net/url" + "slices" "strings" "github.com/pkg/errors" "github.com/mattermost/mattermost/server/public/model" - pUtils "github.com/mattermost/mattermost/server/public/utils" ) // RemoveStringFromSlice removes the first occurrence of a from slice. @@ -32,7 +32,7 @@ func RemoveStringsFromSlice(slice []string, strings ...string) []string { newSlice := []string{} for _, item := range slice { - if !pUtils.Contains(strings, item) { + if !slices.Contains(strings, item) { newSlice = append(newSlice, item) } } @@ -87,17 +87,8 @@ func StringSliceDiff(a, b []string) []string { return result } -func InsertElementToSliceAtIndex[T comparable](slice []T, element T, index int) []T { - if len(slice) == index { - return append(slice, element) - } - slice = append(slice[:index+1], slice[index:]...) - slice[index] = element - return slice -} - -func RemoveElementFromSliceAtIndex[T comparable](slice []T, index int) []T { - return append(slice[:index], slice[index+1:]...) +func RemoveElementFromSliceAtIndex[S ~[]E, E any](slice S, index int) S { + return slices.Delete(slice, index, index+1) } func GetIPAddress(r *http.Request, trustedProxyIPHeader []string) string { @@ -252,24 +243,11 @@ func RoundOffToZeroes(n float64) int64 { return firstDigit * tens } -func MinInt(a, b int) int { - if a < b { - return a - } - return b -} -func MaxInt(a, b int) int { - if a > b { - return a - } - return b -} - // RoundOffToZeroesResolution truncates off at most minResolution zero places. // It implicitly sets the lowest minResolution to 0. // e.g. 0 reports 1s, 1 reports 10s, 2 reports 100s, 3 reports 1000s func RoundOffToZeroesResolution(n float64, minResolution int) int64 { - resolution := MaxInt(0, minResolution) + resolution := max(0, minResolution) if n >= -9 && n <= 9 { if resolution == 0 { return int64(n) @@ -278,7 +256,7 @@ func RoundOffToZeroesResolution(n float64, minResolution int) int64 { } zeroes := int(math.Log10(math.Abs(n))) - resolution = MinInt(zeroes, resolution) + resolution = min(zeroes, resolution) tens := int64(math.Pow10(resolution)) significantDigits := int64(n) / tens return significantDigits * tens diff --git a/server/cmd/mmctl/commands/sampledata.go b/server/cmd/mmctl/commands/sampledata.go index 337a253833..34c1825c54 100644 --- a/server/cmd/mmctl/commands/sampledata.go +++ b/server/cmd/mmctl/commands/sampledata.go @@ -11,6 +11,7 @@ import ( "math/rand" "os" "path/filepath" + "slices" "sort" "time" @@ -360,7 +361,7 @@ func sampledataCmdF(c client.Client, command *cobra.Command, args []string) erro totalUsers := 3 + rand.Intn(3) for len(users) < totalUsers { user := allUsers[rand.Intn(len(allUsers))] - if !pUtils.Contains(users, user) { + if !slices.Contains(users, user) { users = append(users, user) } } @@ -375,7 +376,7 @@ func sampledataCmdF(c client.Client, command *cobra.Command, args []string) erro totalUsers := 3 + rand.Intn(3) for len(users) < totalUsers { user := allUsers[rand.Intn(len(allUsers))] - if !pUtils.Contains(users, user) { + if !slices.Contains(users, user) { users = append(users, user) } } diff --git a/server/public/model/command_autocomplete.go b/server/public/model/command_autocomplete.go index 9b7dec08f9..9e12d6b081 100644 --- a/server/public/model/command_autocomplete.go +++ b/server/public/model/command_autocomplete.go @@ -8,11 +8,10 @@ import ( "net/url" "path" "reflect" + "slices" "strings" "github.com/pkg/errors" - - "github.com/mattermost/mattermost/server/public/utils" ) // AutocompleteArgType describes autocomplete argument type @@ -235,7 +234,7 @@ func (ad *AutocompleteData) IsValid() error { return errors.New("Command should be lowercase") } roles := []string{SystemAdminRoleId, SystemUserRoleId, ""} - if !utils.Contains(roles, ad.RoleID) { + if !slices.Contains(roles, ad.RoleID) { return errors.New("Wrong role in the autocomplete data") } if len(ad.Arguments) > 0 && len(ad.SubCommands) > 0 { diff --git a/server/public/model/report.go b/server/public/model/report.go index 7a9ebb407a..e8587e448f 100644 --- a/server/public/model/report.go +++ b/server/public/model/report.go @@ -5,10 +5,9 @@ package model import ( "net/http" + "slices" "strconv" "time" - - pUtils "github.com/mattermost/mattermost/server/public/utils" ) const ( @@ -137,7 +136,7 @@ func (u *UserReportOptions) IsValid() *AppError { } // Validate against the columns we allow sorting for - if !pUtils.Contains(UserReportSortColumns, u.SortColumn) { + if !slices.Contains(UserReportSortColumns, u.SortColumn) { return NewAppError("UserReportOptions.IsValid", "model.user_report_options.is_valid.invalid_sort_column", nil, "", http.StatusBadRequest) } diff --git a/server/public/model/websocket_message.go b/server/public/model/websocket_message.go index 44b3146d6c..51278b1160 100644 --- a/server/public/model/websocket_message.go +++ b/server/public/model/websocket_message.go @@ -6,6 +6,7 @@ package model import ( "encoding/json" "io" + "maps" "strconv" ) @@ -272,7 +273,7 @@ func (ev *WebSocketEvent) Copy() *WebSocketEvent { func (ev *WebSocketEvent) DeepCopy() *WebSocketEvent { evCopy := &WebSocketEvent{ event: ev.event, - data: copyMap(ev.data), + data: maps.Clone(ev.data), broadcast: ev.broadcast.copy(), sequence: ev.sequence, precomputedJSON: ev.precomputedJSON.copy(), @@ -280,14 +281,6 @@ func (ev *WebSocketEvent) DeepCopy() *WebSocketEvent { return evCopy } -func copyMap[K comparable, V any](m map[K]V) map[K]V { - dataCopy := make(map[K]V, len(m)) - for k, v := range m { - dataCopy[k] = v - } - return dataCopy -} - func (ev *WebSocketEvent) GetData() map[string]any { return ev.data } diff --git a/server/public/pluginapi/kv_memory.go b/server/public/pluginapi/kv_memory.go index f834ea8751..8148b356f6 100644 --- a/server/public/pluginapi/kv_memory.go +++ b/server/public/pluginapi/kv_memory.go @@ -3,7 +3,7 @@ package pluginapi import ( "bytes" "encoding/json" - "sort" + "slices" "strings" "sync" "time" @@ -193,8 +193,7 @@ func (s *MemoryStore) ListKeys(page int, count int, options ...ListKeysOption) ( return []string{}, nil } - // TODO: Use slices.Sort once the toolchain got updated to go1.21 - sort.Strings(allKeys) + slices.Sort(allKeys) pageKeys := paginateSlice(allKeys, page, count) diff --git a/server/public/utils/utils.go b/server/public/utils/utils.go deleted file mode 100644 index fcadce230e..0000000000 --- a/server/public/utils/utils.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package utils - -// Contains returns true if the slice contains the item. -func Contains[T comparable](slice []T, item T) bool { - for _, s := range slice { - if s == item { - return true - } - } - return false -} diff --git a/server/public/utils/utils_test.go b/server/public/utils/utils_test.go deleted file mode 100644 index 856f76886c..0000000000 --- a/server/public/utils/utils_test.go +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package utils_test - -import ( - "testing" - - "github.com/mattermost/mattermost/server/public/utils" -) - -func TestContains(t *testing.T) { - testCasesStr := []struct { - name string - slice []string - item string - expected bool - }{ - { - name: "empty slice", - slice: []string{}, - item: "foo", - expected: false, - }, - { - name: "slice with item", - slice: []string{"foo"}, - item: "foo", - expected: true, - }, - { - name: "slice without item", - slice: []string{"bar"}, - item: "foo", - expected: false, - }, - { - name: "slice with multiple items", - slice: []string{"foo", "bar"}, - item: "foo", - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []string{"foo", "bar"}, - item: "baz", - expected: false, - }, - } - - for _, tc := range testCasesStr { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } - - testCasesInt := []struct { - name string - slice []int - item int - expected bool - }{ - { - name: "empty slice", - slice: []int{}, - item: 1, - expected: false, - }, - { - name: "slice with item", - slice: []int{1}, - item: 1, - expected: true, - }, - { - name: "slice without item", - slice: []int{2}, - item: 1, - expected: false, - }, - { - name: "slice with multiple items", - slice: []int{1, 2}, - item: 1, - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []int{1, 2}, - item: 3, - expected: false, - }, - } - - for _, tc := range testCasesInt { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } - - testCasesFloat := []struct { - name string - slice []float64 - item float64 - expected bool - }{ - { - name: "empty slice", - slice: []float64{}, - item: 1.0, - expected: false, - }, - { - name: "slice with item", - slice: []float64{1.0}, - item: 1.0, - expected: true, - }, - { - name: "slice without item", - slice: []float64{2.0}, - item: 1.0, - expected: false, - }, - { - name: "slice with multiple items", - slice: []float64{1.0, 2.0}, - item: 1.0, - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []float64{1.0, 2.0}, - item: 3.0, - expected: false, - }, - } - - for _, tc := range testCasesFloat { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } - - testCasesBool := []struct { - name string - slice []bool - item bool - expected bool - }{ - { - name: "empty slice", - slice: []bool{}, - item: true, - expected: false, - }, - { - name: "slice with item", - slice: []bool{true}, - item: true, - expected: true, - }, - { - name: "slice without item", - slice: []bool{false}, - item: true, - expected: false, - }, - { - name: "slice with multiple items", - slice: []bool{true, false}, - item: true, - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []bool{true, false}, - item: false, - expected: true, - }, - } - - for _, tc := range testCasesBool { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } - - testCasesByte := []struct { - name string - slice []byte - item byte - expected bool - }{ - { - name: "empty slice", - slice: []byte{}, - item: 1, - expected: false, - }, - { - name: "slice with item", - slice: []byte{1}, - item: 1, - expected: true, - }, - { - name: "slice without item", - slice: []byte{2}, - item: 1, - expected: false, - }, - { - name: "slice with multiple items", - slice: []byte{1, 2}, - item: 1, - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []byte{1, 2}, - item: 3, - expected: false, - }, - } - - for _, tc := range testCasesByte { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } - - testCasesRune := []struct { - name string - slice []rune - item rune - expected bool - }{ - { - name: "empty slice", - slice: []rune{}, - item: 1, - expected: false, - }, - { - name: "slice with item", - slice: []rune{1}, - item: 1, - expected: true, - }, - { - name: "slice without item", - slice: []rune{2}, - item: 1, - expected: false, - }, - { - name: "slice with multiple items", - slice: []rune{1, 2}, - item: 1, - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []rune{1, 2}, - item: 3, - expected: false, - }, - } - - for _, tc := range testCasesRune { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } - - testCasesComplex := []struct { - name string - slice []complex128 - item complex128 - expected bool - }{ - { - name: "empty slice", - slice: []complex128{}, - item: 1, - expected: false, - }, - { - name: "slice with item", - slice: []complex128{1}, - item: 1, - expected: true, - }, - { - name: "slice without item", - slice: []complex128{2}, - item: 1, - expected: false, - }, - { - name: "slice with multiple items", - slice: []complex128{1, 2}, - item: 1, - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []complex128{1, 2}, - item: 3, - expected: false, - }, - } - - for _, tc := range testCasesComplex { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } - - testCasesUint := []struct { - name string - slice []uint - item uint - expected bool - }{ - { - name: "empty slice", - slice: []uint{}, - item: 1, - expected: false, - }, - { - name: "slice with item", - slice: []uint{1}, - item: 1, - expected: true, - }, - { - name: "slice without item", - slice: []uint{2}, - item: 1, - expected: false, - }, - { - name: "slice with multiple items", - slice: []uint{1, 2}, - item: 1, - expected: true, - }, - { - name: "slice with multiple items without item", - slice: []uint{1, 2}, - item: 3, - expected: false, - }, - } - - for _, tc := range testCasesUint { - t.Run(tc.name, func(t *testing.T) { - actual := utils.Contains(tc.slice, tc.item) - if actual != tc.expected { - t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual) - } - }) - } -}