diff --git a/models/actions/run.go b/models/actions/run.go index 18ed447e8..4853afc15 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -6,6 +6,7 @@ package actions import ( "context" "fmt" + "slices" "strings" "time" @@ -350,7 +351,7 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error { // It's impossible that the run is not found, since Gitea never deletes runs. } - if run.Status != 0 || util.SliceContains(cols, "status") { + if run.Status != 0 || slices.Contains(cols, "status") { if run.RepoID == 0 { run, err = GetRunByID(ctx, run.ID) if err != nil { diff --git a/models/actions/run_job.go b/models/actions/run_job.go index 1da58bb65..4b8664077 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -6,6 +6,7 @@ package actions import ( "context" "fmt" + "slices" "time" "code.gitea.io/gitea/models/db" @@ -107,11 +108,11 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col return 0, err } - if affected == 0 || (!util.SliceContains(cols, "status") && job.Status == 0) { + if affected == 0 || (!slices.Contains(cols, "status") && job.Status == 0) { return affected, nil } - if affected != 0 && util.SliceContains(cols, "status") && job.Status.IsWaiting() { + if affected != 0 && slices.Contains(cols, "status") && job.Status.IsWaiting() { // if the status of job changes to waiting again, increase tasks version. if err := IncreaseTaskVersion(ctx, job.OwnerID, job.RepoID); err != nil { return 0, err diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index eef7e3935..5ed100374 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "slices" "strings" "code.gitea.io/gitea/models/db" @@ -435,7 +436,7 @@ func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, curre whitelist = make([]int64, 0, len(teams)) for i := range teams { - if util.SliceContains(newWhitelist, teams[i].ID) { + if slices.Contains(newWhitelist, teams[i].ID) { whitelist = append(whitelist, teams[i].ID) } } diff --git a/models/issues/issue.go b/models/issues/issue.go index f000f4c66..8f59c9cb4 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "regexp" + "slices" "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" @@ -605,7 +606,7 @@ func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool { log.Error(err.Error()) return false } - return util.SliceContains(userIDs, user.ID) + return slices.Contains(userIDs, user.ID) } // DependencyInfo represents high level information about an issue which is a dependency of another issue. @@ -630,7 +631,7 @@ func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, erro Find(&userIDs); err != nil { return nil, fmt.Errorf("get poster IDs: %w", err) } - if !util.SliceContains(userIDs, issue.PosterID) { + if !slices.Contains(userIDs, issue.PosterID) { return append(userIDs, issue.PosterID), nil } return userIDs, nil diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index cf9ff93d3..b8804c6df 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -6,6 +6,7 @@ package repo import ( "context" "fmt" + "slices" "strings" "code.gitea.io/gitea/models/db" @@ -176,7 +177,7 @@ func (cfg *ActionsConfig) ToString() string { } func (cfg *ActionsConfig) IsWorkflowDisabled(file string) bool { - return util.SliceContains(cfg.DisabledWorkflows, file) + return slices.Contains(cfg.DisabledWorkflows, file) } func (cfg *ActionsConfig) DisableWorkflow(file string) { diff --git a/modules/indexer/code/indexer.go b/modules/indexer/code/indexer.go index 13d06874c..80bde7614 100644 --- a/modules/indexer/code/indexer.go +++ b/modules/indexer/code/indexer.go @@ -7,6 +7,7 @@ import ( "context" "os" "runtime/pprof" + "slices" "sync/atomic" "time" @@ -20,7 +21,6 @@ import ( "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" ) var ( @@ -54,22 +54,22 @@ func index(ctx context.Context, indexer internal.Indexer, repoID int64) error { } // skip forks from being indexed if unit is not present - if !util.SliceContains(repoTypes, "forks") && repo.IsFork { + if !slices.Contains(repoTypes, "forks") && repo.IsFork { return nil } // skip mirrors from being indexed if unit is not present - if !util.SliceContains(repoTypes, "mirrors") && repo.IsMirror { + if !slices.Contains(repoTypes, "mirrors") && repo.IsMirror { return nil } // skip templates from being indexed if unit is not present - if !util.SliceContains(repoTypes, "templates") && repo.IsTemplate { + if !slices.Contains(repoTypes, "templates") && repo.IsTemplate { return nil } // skip regular repos from being indexed if unit is not present - if !util.SliceContains(repoTypes, "sources") && !repo.IsFork && !repo.IsMirror && !repo.IsTemplate { + if !slices.Contains(repoTypes, "sources") && !repo.IsFork && !repo.IsMirror && !repo.IsTemplate { return nil } diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 93d38a0b3..06fddeb65 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -10,6 +10,7 @@ package tests import ( "context" "fmt" + "slices" "testing" "time" @@ -457,7 +458,7 @@ var cases = []*testIndexerCase{ assert.Contains(t, data[v.ID].MentionIDs, int64(1)) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return util.SliceContains(v.MentionIDs, 1) + return slices.Contains(v.MentionIDs, 1) }), result.Total) }, }, @@ -478,7 +479,7 @@ var cases = []*testIndexerCase{ assert.Contains(t, data[v.ID].ReviewedIDs, int64(1)) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return util.SliceContains(v.ReviewedIDs, 1) + return slices.Contains(v.ReviewedIDs, 1) }), result.Total) }, }, @@ -499,7 +500,7 @@ var cases = []*testIndexerCase{ assert.Contains(t, data[v.ID].ReviewRequestedIDs, int64(1)) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return util.SliceContains(v.ReviewRequestedIDs, 1) + return slices.Contains(v.ReviewRequestedIDs, 1) }), result.Total) }, }, @@ -520,7 +521,7 @@ var cases = []*testIndexerCase{ assert.Contains(t, data[v.ID].SubscriberIDs, int64(1)) } assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { - return util.SliceContains(v.SubscriberIDs, 1) + return slices.Contains(v.SubscriberIDs, 1) }), result.Total) }, }, diff --git a/modules/templates/base.go b/modules/templates/base.go index ef28cc03f..2c2f35bbe 100644 --- a/modules/templates/base.go +++ b/modules/templates/base.go @@ -4,11 +4,11 @@ package templates import ( + "slices" "strings" "code.gitea.io/gitea/modules/assetfs" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" ) func AssetFS() *assetfs.LayeredFS { @@ -24,7 +24,7 @@ func ListWebTemplateAssetNames(assets *assetfs.LayeredFS) ([]string, error) { if err != nil { return nil, err } - return util.SliceRemoveAllFunc(files, func(file string) bool { + return slices.DeleteFunc(files, func(file string) bool { return strings.HasPrefix(file, "mail/") || !strings.HasSuffix(file, ".tmpl") }), nil } @@ -34,7 +34,7 @@ func ListMailTemplateAssetNames(assets *assetfs.LayeredFS) ([]string, error) { if err != nil { return nil, err } - return util.SliceRemoveAllFunc(files, func(file string) bool { + return slices.DeleteFunc(files, func(file string) bool { return !strings.HasPrefix(file, "mail/") || !strings.HasSuffix(file, ".tmpl") }), nil } diff --git a/modules/util/slice.go b/modules/util/slice.go index 74356f549..6d63ab4a7 100644 --- a/modules/util/slice.go +++ b/modules/util/slice.go @@ -1,37 +1,21 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -// Most of the functions in this file can have better implementations with "golang.org/x/exp/slices". -// However, "golang.org/x/exp" is experimental and unreliable, we shouldn't use it. -// So lets waiting for the "slices" has be promoted to the main repository one day. - package util -import "strings" - -// SliceContains returns true if the target exists in the slice. -func SliceContains[T comparable](slice []T, target T) bool { - return SliceContainsFunc(slice, func(t T) bool { return t == target }) -} - -// SliceContainsFunc returns true if any element in the slice satisfies the targetFunc. -func SliceContainsFunc[T any](slice []T, targetFunc func(T) bool) bool { - for _, v := range slice { - if targetFunc(v) { - return true - } - } - return false -} +import ( + "slices" + "strings" +) // SliceContainsString sequential searches if string exists in slice. func SliceContainsString(slice []string, target string, insensitive ...bool) bool { if len(insensitive) != 0 && insensitive[0] { target = strings.ToLower(target) - return SliceContainsFunc(slice, func(t string) bool { return strings.ToLower(t) == target }) + return slices.ContainsFunc(slice, func(t string) bool { return strings.ToLower(t) == target }) } - return SliceContains(slice, target) + return slices.Contains(slice, target) } // SliceSortedEqual returns true if the two slices will be equal when they get sorted. @@ -57,34 +41,7 @@ func SliceSortedEqual[T comparable](s1, s2 []T) bool { return true } -// SliceEqual returns true if the two slices are equal. -func SliceEqual[T comparable](s1, s2 []T) bool { - if len(s1) != len(s2) { - return false - } - - for i, v := range s1 { - if s2[i] != v { - return false - } - } - return true -} - // SliceRemoveAll removes all the target elements from the slice. func SliceRemoveAll[T comparable](slice []T, target T) []T { - return SliceRemoveAllFunc(slice, func(t T) bool { return t == target }) -} - -// SliceRemoveAllFunc removes all elements which satisfy the targetFunc from the slice. -func SliceRemoveAllFunc[T comparable](slice []T, targetFunc func(T) bool) []T { - idx := 0 - for _, v := range slice { - if targetFunc(v) { - continue - } - slice[idx] = v - idx++ - } - return slice[:idx] + return slices.DeleteFunc(slice, func(t T) bool { return t == target }) } diff --git a/modules/util/slice_test.go b/modules/util/slice_test.go index 373c1a3b7..a910f5edf 100644 --- a/modules/util/slice_test.go +++ b/modules/util/slice_test.go @@ -9,20 +9,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSliceContains(t *testing.T) { - assert.True(t, SliceContains([]int{2, 0, 2, 3}, 2)) - assert.True(t, SliceContains([]int{2, 0, 2, 3}, 0)) - assert.True(t, SliceContains([]int{2, 0, 2, 3}, 3)) - - assert.True(t, SliceContains([]string{"2", "0", "2", "3"}, "0")) - assert.True(t, SliceContains([]float64{2, 0, 2, 3}, 0)) - assert.True(t, SliceContains([]bool{false, true, false}, true)) - - assert.False(t, SliceContains([]int{2, 0, 2, 3}, 4)) - assert.False(t, SliceContains([]int{}, 4)) - assert.False(t, SliceContains(nil, 4)) -} - func TestSliceContainsString(t *testing.T) { assert.True(t, SliceContainsString([]string{"c", "b", "a", "b"}, "a")) assert.True(t, SliceContainsString([]string{"c", "b", "a", "b"}, "b")) @@ -54,25 +40,6 @@ func TestSliceSortedEqual(t *testing.T) { assert.False(t, SliceSortedEqual([]int{2, 0, 0, 3}, []int{2, 0, 2, 3})) } -func TestSliceEqual(t *testing.T) { - assert.True(t, SliceEqual([]int{2, 0, 2, 3}, []int{2, 0, 2, 3})) - assert.True(t, SliceEqual([]int{}, []int{})) - assert.True(t, SliceEqual([]int(nil), nil)) - assert.True(t, SliceEqual([]int(nil), []int{})) - assert.True(t, SliceEqual([]int{}, []int{})) - - assert.True(t, SliceEqual([]string{"2", "0", "2", "3"}, []string{"2", "0", "2", "3"})) - assert.True(t, SliceEqual([]float64{2, 0, 2, 3}, []float64{2, 0, 2, 3})) - assert.True(t, SliceEqual([]bool{false, true, false}, []bool{false, true, false})) - - assert.False(t, SliceEqual([]int{3, 0, 2, 2}, []int{2, 0, 2, 3})) - assert.False(t, SliceEqual([]int{2, 0, 2}, []int{2, 0, 2, 3})) - assert.False(t, SliceEqual([]int{}, []int{2, 0, 2, 3})) - assert.False(t, SliceEqual(nil, []int{2, 0, 2, 3})) - assert.False(t, SliceEqual([]int{2, 0, 2, 4}, []int{2, 0, 2, 3})) - assert.False(t, SliceEqual([]int{2, 0, 0, 3}, []int{2, 0, 2, 3})) -} - func TestSliceRemoveAll(t *testing.T) { assert.ElementsMatch(t, []int{2, 2, 3}, SliceRemoveAll([]int{2, 0, 2, 3}, 0)) assert.ElementsMatch(t, []int{0, 3}, SliceRemoveAll([]int{2, 0, 2, 3}, 2)) diff --git a/routers/api/packages/chef/auth.go b/routers/api/packages/chef/auth.go index d89564089..53055bb68 100644 --- a/routers/api/packages/chef/auth.go +++ b/routers/api/packages/chef/auth.go @@ -16,6 +16,7 @@ import ( "net/http" "path" "regexp" + "slices" "strconv" "strings" "time" @@ -265,7 +266,7 @@ func verifyDataOld(signature, data []byte, pub *rsa.PublicKey) error { } } - if !util.SliceEqual(out[skip:], data) { + if !slices.Equal(out[skip:], data) { return fmt.Errorf("could not verify signature") } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 29f6a675d..e86743d55 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -7,6 +7,7 @@ package repo import ( "fmt" "net/http" + "slices" "strings" "time" @@ -235,7 +236,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre } // If the readme template does not exist, a 400 will be returned. - if opt.AutoInit && len(opt.Readme) > 0 && !util.SliceContains(repo_module.Readmes, opt.Readme) { + if opt.AutoInit && len(opt.Readme) > 0 && !slices.Contains(repo_module.Readmes, opt.Readme) { ctx.Error(http.StatusBadRequest, "", fmt.Errorf("readme template does not exist, available templates: %v", repo_module.Readmes)) return } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index f5e8f80dd..c95d54532 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -12,6 +12,7 @@ import ( "math/big" "net/http" "net/url" + "slices" "sort" "strconv" "strings" @@ -3628,7 +3629,7 @@ func issuePosters(ctx *context.Context, isPullList bool) { if search == "" && ctx.Doer != nil { // the returned posters slice only contains limited number of users, // to make the current user (doer) can quickly filter their own issues, always add doer to the posters slice - if !util.SliceContainsFunc(posters, func(user *user_model.User) bool { return user.ID == ctx.Doer.ID }) { + if !slices.ContainsFunc(posters, func(user *user_model.User) bool { return user.ID == ctx.Doer.ID }) { posters = append(posters, ctx.Doer) } } diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 12cd47792..3ed7ca1c9 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net/http" + "slices" "strings" "code.gitea.io/gitea/models" @@ -659,7 +660,7 @@ func GetBranchesList(ctx *context.Context) { } resp := &branchTagSearchResponse{} // always put default branch on the top if it exists - if util.SliceContains(branches, ctx.Repo.Repository.DefaultBranch) { + if slices.Contains(branches, ctx.Repo.Repository.DefaultBranch) { branches = util.SliceRemoveAll(branches, ctx.Repo.Repository.DefaultBranch) branches = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...) } @@ -693,7 +694,7 @@ func PrepareBranchList(ctx *context.Context) { return } // always put default branch on the top if it exists - if util.SliceContains(brs, ctx.Repo.Repository.DefaultBranch) { + if slices.Contains(brs, ctx.Repo.Repository.DefaultBranch) { brs = util.SliceRemoveAll(brs, ctx.Repo.Repository.DefaultBranch) brs = append([]string{ctx.Repo.Repository.DefaultBranch}, brs...) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 15c85f642..f0fe6140d 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -14,6 +14,7 @@ import ( "net/http" "net/url" "path" + "slices" "strings" "time" @@ -370,7 +371,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st if workFlowErr != nil { ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) } - } else if util.SliceContains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { + } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { if data, err := blob.GetBlobContent(setting.UI.MaxDisplayFileSize); err == nil { _, warnings := issue_model.GetCodeOwnersFromContent(ctx, data) if len(warnings) > 0 { diff --git a/routers/web/user/home.go b/routers/web/user/home.go index a7f6a52f1..a88479e12 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -9,6 +9,7 @@ import ( "fmt" "net/http" "regexp" + "slices" "sort" "strconv" "strings" @@ -290,7 +291,7 @@ func Milestones(ctx *context.Context) { if len(repoIDs) == 0 { repoIDs = showRepoIds.Values() } - repoIDs = util.SliceRemoveAllFunc(repoIDs, func(v int64) bool { + repoIDs = slices.DeleteFunc(repoIDs, func(v int64) bool { return !showRepoIds.Contains(v) }) @@ -534,7 +535,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // Gets set when clicking filters on the issues overview page. selectedRepoIDs := getRepoIDs(ctx.FormString("repos")) // Remove repo IDs that are not accessible to the user. - selectedRepoIDs = util.SliceRemoveAllFunc(selectedRepoIDs, func(v int64) bool { + selectedRepoIDs = slices.DeleteFunc(selectedRepoIDs, func(v int64) bool { return !accessibleRepos.Contains(v) }) if len(selectedRepoIDs) > 0 {