Improve utils of slices (#22379)

- Move the file `compare.go` and `slice.go` to `slice.go`.
- Fix `ExistsInSlice`, it's buggy
  - It uses `sort.Search`, so it assumes that the input slice is sorted.
- It passes `func(i int) bool { return slice[i] == target })` to
`sort.Search`, that's incorrect, check the doc of `sort.Search`.
- Conbine `IsInt64InSlice(int64, []int64)` and `ExistsInSlice(string,
[]string)` to `SliceContains[T]([]T, T)`.
- Conbine `IsSliceInt64Eq([]int64, []int64)` and `IsEqualSlice([]string,
[]string)` to `SliceSortedEqual[T]([]T, T)`.
- Add `SliceEqual[T]([]T, T)` as a distinction from
`SliceSortedEqual[T]([]T, T)`.
- Redesign `RemoveIDFromList([]int64, int64) ([]int64, bool)` to
`SliceRemoveAll[T]([]T, T) []T`.
- Add `SliceContainsFunc[T]([]T, func(T) bool)` and
`SliceRemoveAllFunc[T]([]T, func(T) bool)` for general use.
- Add comments to explain why not `golang.org/x/exp/slices`.
- Add unit tests.
This commit is contained in:
Jason Song 2023-01-11 13:31:16 +08:00 committed by GitHub
parent dc5f2cf590
commit 477a1cc40e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 228 additions and 182 deletions

View file

@ -950,7 +950,7 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
if c.IsSet("auth-type") { if c.IsSet("auth-type") {
conf.Auth = c.String("auth-type") conf.Auth = c.String("auth-type")
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"} validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
if !contains(validAuthTypes, strings.ToUpper(c.String("auth-type"))) { if !util.SliceContainsString(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5") return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
} }
conf.Auth = c.String("auth-type") conf.Auth = c.String("auth-type")

View file

@ -409,15 +409,6 @@ func runDump(ctx *cli.Context) error {
return nil return nil
} }
func contains(slice []string, s string) bool {
for _, v := range slice {
if v == s {
return true
}
}
return false
}
// addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath // addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath
func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error { func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error {
absPath, err := filepath.Abs(absPath) absPath, err := filepath.Abs(absPath)
@ -438,7 +429,7 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
currentAbsPath := path.Join(absPath, file.Name()) currentAbsPath := path.Join(absPath, file.Name())
currentInsidePath := path.Join(insidePath, file.Name()) currentInsidePath := path.Join(insidePath, file.Name())
if file.IsDir() { if file.IsDir() {
if !contains(excludeAbsPath, currentAbsPath) { if !util.SliceContainsString(excludeAbsPath, currentAbsPath) {
if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil { if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil {
return err return err
} }

View file

@ -409,14 +409,14 @@ func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys [
sshKeySplit := strings.Split(v, " ") sshKeySplit := strings.Split(v, " ")
if len(sshKeySplit) > 1 { if len(sshKeySplit) > 1 {
key := strings.Join(sshKeySplit[:2], " ") key := strings.Join(sshKeySplit[:2], " ")
if !util.ExistsInSlice(key, providedKeys) { if !util.SliceContainsString(providedKeys, key) {
providedKeys = append(providedKeys, key) providedKeys = append(providedKeys, key)
} }
} }
} }
// Check if Public Key sync is needed // Check if Public Key sync is needed
if util.IsEqualSlice(giteaKeys, providedKeys) { if util.SliceSortedEqual(giteaKeys, providedKeys) {
log.Trace("synchronizePublicKeys[%s]: Public Keys are already in sync for %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys)) log.Trace("synchronizePublicKeys[%s]: Public Keys are already in sync for %s (Source:%v/DB:%v)", s.Name, usr.Name, len(providedKeys), len(giteaKeys))
return false return false
} }
@ -425,7 +425,7 @@ func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys [
// Add new Public SSH Keys that doesn't already exist in DB // Add new Public SSH Keys that doesn't already exist in DB
var newKeys []string var newKeys []string
for _, key := range providedKeys { for _, key := range providedKeys {
if !util.ExistsInSlice(key, giteaKeys) { if !util.SliceContainsString(giteaKeys, key) {
newKeys = append(newKeys, key) newKeys = append(newKeys, key)
} }
} }
@ -436,7 +436,7 @@ func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys [
// Mark keys from DB that no longer exist in the source for deletion // Mark keys from DB that no longer exist in the source for deletion
var giteaKeysToDelete []string var giteaKeysToDelete []string
for _, giteaKey := range giteaKeys { for _, giteaKey := range giteaKeys {
if !util.ExistsInSlice(giteaKey, providedKeys) { if !util.SliceContainsString(providedKeys, giteaKey) {
log.Trace("synchronizePublicKeys[%s]: Marking Public SSH Key for deletion for user %s: %v", s.Name, usr.Name, giteaKey) log.Trace("synchronizePublicKeys[%s]: Marking Public SSH Key for deletion for user %s: %v", s.Name, usr.Name, giteaKey)
giteaKeysToDelete = append(giteaKeysToDelete, giteaKey) giteaKeysToDelete = append(giteaKeysToDelete, giteaKey)
} }

View file

@ -69,13 +69,13 @@ func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
if ip != nil && ip.IsLoopback() { if ip != nil && ip.IsLoopback() {
// strip port // strip port
uri.Host = uri.Hostname() uri.Host = uri.Hostname()
if util.IsStringInSlice(uri.String(), app.RedirectURIs, true) { if util.SliceContainsString(app.RedirectURIs, uri.String(), true) {
return true return true
} }
} }
} }
} }
return util.IsStringInSlice(redirectURI, app.RedirectURIs, true) return util.SliceContainsString(app.RedirectURIs, redirectURI, true)
} }
// Base32 characters, but lowercased. // Base32 characters, but lowercased.

View file

@ -342,7 +342,7 @@ func IsProtectedBranch(ctx context.Context, repoID int64, branchName string) (bo
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with // updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
// the users from newWhitelist which have explicit read or write access to the repo. // the users from newWhitelist which have explicit read or write access to the repo.
func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) { func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
hasUsersChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist) hasUsersChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
if !hasUsersChanged { if !hasUsersChanged {
return currentWhitelist, nil return currentWhitelist, nil
} }
@ -363,7 +363,7 @@ func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, c
// updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with // updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
// the users from newWhitelist which have write access to the repo. // the users from newWhitelist which have write access to the repo.
func updateUserWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) { func updateUserWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
hasUsersChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist) hasUsersChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
if !hasUsersChanged { if !hasUsersChanged {
return currentWhitelist, nil return currentWhitelist, nil
} }
@ -392,7 +392,7 @@ func updateUserWhitelist(ctx context.Context, repo *repo_model.Repository, curre
// updateTeamWhitelist checks whether the team whitelist changed and returns a whitelist with // updateTeamWhitelist checks whether the team whitelist changed and returns a whitelist with
// the teams from newWhitelist which have write access to the repo. // the teams from newWhitelist which have write access to the repo.
func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) { func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
hasTeamsChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist) hasTeamsChanged := !util.SliceSortedEqual(currentWhitelist, newWhitelist)
if !hasTeamsChanged { if !hasTeamsChanged {
return currentWhitelist, nil return currentWhitelist, nil
} }
@ -404,7 +404,7 @@ func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, curre
whitelist = make([]int64, 0, len(teams)) whitelist = make([]int64, 0, len(teams))
for i := range teams { for i := range teams {
if util.IsInt64InSlice(teams[i].ID, newWhitelist) { if util.SliceContains(newWhitelist, teams[i].ID) {
whitelist = append(whitelist, teams[i].ID) whitelist = append(whitelist, teams[i].ID)
} }
} }

View file

@ -155,7 +155,7 @@ func MakeIDsFromAPIAssigneesToAdd(ctx context.Context, oneAssignee string, multi
var requestAssignees []string var requestAssignees []string
// Keeping the old assigning method for compatibility reasons // Keeping the old assigning method for compatibility reasons
if oneAssignee != "" && !util.IsStringInSlice(oneAssignee, multipleAssignees) { if oneAssignee != "" && !util.SliceContainsString(multipleAssignees, oneAssignee) {
requestAssignees = append(requestAssignees, oneAssignee) requestAssignees = append(requestAssignees, oneAssignee)
} }

View file

@ -1529,7 +1529,7 @@ func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool {
log.Error(err.Error()) log.Error(err.Error())
return false return false
} }
return util.IsInt64InSlice(user.ID, userIDs) return util.SliceContains(userIDs, user.ID)
} }
// UpdateIssueMentions updates issue-user relations for mentioned users. // UpdateIssueMentions updates issue-user relations for mentioned users.
@ -2023,7 +2023,7 @@ func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, erro
Find(&userIDs); err != nil { Find(&userIDs); err != nil {
return nil, fmt.Errorf("get poster IDs: %w", err) return nil, fmt.Errorf("get poster IDs: %w", err)
} }
if !util.IsInt64InSlice(issue.PosterID, userIDs) { if !util.SliceContains(userIDs, issue.PosterID) {
return append(userIDs, issue.PosterID), nil return append(userIDs, issue.PosterID), nil
} }
return userIDs, nil return userIDs, nil

View file

@ -398,20 +398,13 @@ func DeleteTeam(t *organization.Team) error {
return fmt.Errorf("findProtectedBranches: %w", err) return fmt.Errorf("findProtectedBranches: %w", err)
} }
for _, p := range protections { for _, p := range protections {
var matched1, matched2, matched3 bool lenIDs, lenApprovalIDs, lenMergeIDs := len(p.WhitelistTeamIDs), len(p.ApprovalsWhitelistTeamIDs), len(p.MergeWhitelistTeamIDs)
if len(p.WhitelistTeamIDs) != 0 { p.WhitelistTeamIDs = util.SliceRemoveAll(p.WhitelistTeamIDs, t.ID)
p.WhitelistTeamIDs, matched1 = util.RemoveIDFromList( p.ApprovalsWhitelistTeamIDs = util.SliceRemoveAll(p.ApprovalsWhitelistTeamIDs, t.ID)
p.WhitelistTeamIDs, t.ID) p.MergeWhitelistTeamIDs = util.SliceRemoveAll(p.MergeWhitelistTeamIDs, t.ID)
} if lenIDs != len(p.WhitelistTeamIDs) ||
if len(p.ApprovalsWhitelistTeamIDs) != 0 { lenApprovalIDs != len(p.ApprovalsWhitelistTeamIDs) ||
p.ApprovalsWhitelistTeamIDs, matched2 = util.RemoveIDFromList( lenMergeIDs != len(p.MergeWhitelistTeamIDs) {
p.ApprovalsWhitelistTeamIDs, t.ID)
}
if len(p.MergeWhitelistTeamIDs) != 0 {
p.MergeWhitelistTeamIDs, matched3 = util.RemoveIDFromList(
p.MergeWhitelistTeamIDs, t.ID)
}
if matched1 || matched2 || matched3 {
if _, err = sess.ID(p.ID).Cols( if _, err = sess.ID(p.ID).Cols(
"whitelist_team_i_ds", "whitelist_team_i_ds",
"merge_whitelist_team_i_ds", "merge_whitelist_team_i_ds",

View file

@ -141,20 +141,13 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
break break
} }
for _, p := range protections { for _, p := range protections {
var matched1, matched2, matched3 bool lenIDs, lenApprovalIDs, lenMergeIDs := len(p.WhitelistUserIDs), len(p.ApprovalsWhitelistUserIDs), len(p.MergeWhitelistUserIDs)
if len(p.WhitelistUserIDs) != 0 { p.WhitelistUserIDs = util.SliceRemoveAll(p.WhitelistUserIDs, u.ID)
p.WhitelistUserIDs, matched1 = util.RemoveIDFromList( p.ApprovalsWhitelistUserIDs = util.SliceRemoveAll(p.ApprovalsWhitelistUserIDs, u.ID)
p.WhitelistUserIDs, u.ID) p.MergeWhitelistUserIDs = util.SliceRemoveAll(p.MergeWhitelistUserIDs, u.ID)
} if lenIDs != len(p.WhitelistUserIDs) ||
if len(p.ApprovalsWhitelistUserIDs) != 0 { lenApprovalIDs != len(p.ApprovalsWhitelistUserIDs) ||
p.ApprovalsWhitelistUserIDs, matched2 = util.RemoveIDFromList( lenMergeIDs != len(p.MergeWhitelistUserIDs) {
p.ApprovalsWhitelistUserIDs, u.ID)
}
if len(p.MergeWhitelistUserIDs) != 0 {
p.MergeWhitelistUserIDs, matched3 = util.RemoveIDFromList(
p.MergeWhitelistUserIDs, u.ID)
}
if matched1 || matched2 || matched3 {
if _, err = e.ID(p.ID).Cols( if _, err = e.ID(p.ID).Cols(
"whitelist_user_i_ds", "whitelist_user_i_ds",
"merge_whitelist_user_i_ds", "merge_whitelist_user_i_ds",

View file

@ -170,7 +170,7 @@ func LoadRepoConfig() {
} }
for _, f := range customFiles { for _, f := range customFiles {
if !util.IsStringInSlice(f, files, true) { if !util.SliceContainsString(files, f, true) {
files = append(files, f) files = append(files, f)
} }
} }
@ -200,12 +200,12 @@ func LoadRepoConfig() {
// Filter out invalid names and promote preferred licenses. // Filter out invalid names and promote preferred licenses.
sortedLicenses := make([]string, 0, len(Licenses)) sortedLicenses := make([]string, 0, len(Licenses))
for _, name := range setting.Repository.PreferredLicenses { for _, name := range setting.Repository.PreferredLicenses {
if util.IsStringInSlice(name, Licenses, true) { if util.SliceContainsString(Licenses, name, true) {
sortedLicenses = append(sortedLicenses, name) sortedLicenses = append(sortedLicenses, name)
} }
} }
for _, name := range Licenses { for _, name := range Licenses {
if !util.IsStringInSlice(name, setting.Repository.PreferredLicenses, true) { if !util.SliceContainsString(setting.Repository.PreferredLicenses, name, true) {
sortedLicenses = append(sortedLicenses, name) sortedLicenses = append(sortedLicenses, name)
} }
} }

View file

@ -1,92 +0,0 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package util
import (
"sort"
"strings"
)
// Int64Slice attaches the methods of Interface to []int64, sorting in increasing order.
type Int64Slice []int64
func (p Int64Slice) Len() int { return len(p) }
func (p Int64Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p Int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// IsSliceInt64Eq returns if the two slice has the same elements but different sequences.
func IsSliceInt64Eq(a, b []int64) bool {
if len(a) != len(b) {
return false
}
sort.Sort(Int64Slice(a))
sort.Sort(Int64Slice(b))
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
// ExistsInSlice returns true if string exists in slice.
func ExistsInSlice(target string, slice []string) bool {
i := sort.Search(len(slice),
func(i int) bool { return slice[i] == target })
return i < len(slice)
}
// IsStringInSlice sequential searches if string exists in slice.
func IsStringInSlice(target string, slice []string, insensitive ...bool) bool {
caseInsensitive := false
if len(insensitive) != 0 && insensitive[0] {
caseInsensitive = true
target = strings.ToLower(target)
}
for i := 0; i < len(slice); i++ {
if caseInsensitive {
if strings.ToLower(slice[i]) == target {
return true
}
} else {
if slice[i] == target {
return true
}
}
}
return false
}
// IsInt64InSlice sequential searches if int64 exists in slice.
func IsInt64InSlice(target int64, slice []int64) bool {
for i := 0; i < len(slice); i++ {
if slice[i] == target {
return true
}
}
return false
}
// IsEqualSlice returns true if slices are equal.
func IsEqualSlice(target, source []string) bool {
if len(target) != len(source) {
return false
}
if (target == nil) != (source == nil) {
return false
}
sort.Strings(target)
sort.Strings(source)
for i, v := range target {
if v != source[i] {
return false
}
}
return true
}

View file

@ -1,17 +1,90 @@
// Copyright 2022 The Gitea Authors. All rights reserved. // Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // 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 package util
// RemoveIDFromList removes the given ID from the slice, if found. import "strings"
// It does not preserve order, and assumes the ID is unique.
func RemoveIDFromList(list []int64, id int64) ([]int64, bool) { // SliceContains returns true if the target exists in the slice.
n := len(list) - 1 func SliceContains[T comparable](slice []T, target T) bool {
for i, item := range list { return SliceContainsFunc(slice, func(t T) bool { return t == target })
if item == id { }
list[i] = list[n]
return list[:n], true // 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 list, false return false
}
// 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 SliceContains(slice, target)
}
// SliceSortedEqual returns true if the two slices will be equal when they get sorted.
// It doesn't require that the slices have been sorted, and it doesn't sort them either.
func SliceSortedEqual[T comparable](s1, s2 []T) bool {
if len(s1) != len(s2) {
return false
}
counts := make(map[T]int, len(s1))
for _, v := range s1 {
counts[v]++
}
for _, v := range s2 {
counts[v]--
}
for _, v := range counts {
if v != 0 {
return false
}
}
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]
} }

View file

@ -0,0 +1,88 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package util
import (
"testing"
"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"))
assert.True(t, SliceContainsString([]string{"c", "b", "a", "b"}, "A", true))
assert.True(t, SliceContainsString([]string{"C", "B", "A", "B"}, "a", true))
assert.False(t, SliceContainsString([]string{"c", "b", "a", "b"}, "z"))
assert.False(t, SliceContainsString([]string{"c", "b", "a", "b"}, "A"))
assert.False(t, SliceContainsString([]string{}, "a"))
assert.False(t, SliceContainsString(nil, "a"))
}
func TestSliceSortedEqual(t *testing.T) {
assert.True(t, SliceSortedEqual([]int{2, 0, 2, 3}, []int{2, 0, 2, 3}))
assert.True(t, SliceSortedEqual([]int{3, 0, 2, 2}, []int{2, 0, 2, 3}))
assert.True(t, SliceSortedEqual([]int{}, []int{}))
assert.True(t, SliceSortedEqual([]int(nil), nil))
assert.True(t, SliceSortedEqual([]int(nil), []int{}))
assert.True(t, SliceSortedEqual([]int{}, []int{}))
assert.True(t, SliceSortedEqual([]string{"2", "0", "2", "3"}, []string{"2", "0", "2", "3"}))
assert.True(t, SliceSortedEqual([]float64{2, 0, 2, 3}, []float64{2, 0, 2, 3}))
assert.True(t, SliceSortedEqual([]bool{false, true, false}, []bool{false, true, false}))
assert.False(t, SliceSortedEqual([]int{2, 0, 2}, []int{2, 0, 2, 3}))
assert.False(t, SliceSortedEqual([]int{}, []int{2, 0, 2, 3}))
assert.False(t, SliceSortedEqual(nil, []int{2, 0, 2, 3}))
assert.False(t, SliceSortedEqual([]int{2, 0, 2, 4}, []int{2, 0, 2, 3}))
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.Equal(t, SliceRemoveAll([]int{2, 0, 2, 3}, 0), []int{2, 2, 3})
assert.Equal(t, SliceRemoveAll([]int{2, 0, 2, 3}, 2), []int{0, 3})
assert.Equal(t, SliceRemoveAll([]int{0, 0, 0, 0}, 0), []int{})
assert.Equal(t, SliceRemoveAll([]int{2, 0, 2, 3}, 4), []int{2, 0, 2, 3})
assert.Equal(t, SliceRemoveAll([]int{}, 0), []int{})
assert.Equal(t, SliceRemoveAll([]int(nil), 0), []int(nil))
assert.Equal(t, SliceRemoveAll([]int{}, 0), []int{})
assert.Equal(t, SliceRemoveAll([]string{"2", "0", "2", "3"}, "0"), []string{"2", "2", "3"})
assert.Equal(t, SliceRemoveAll([]float64{2, 0, 2, 3}, 0), []float64{2, 2, 3})
assert.Equal(t, SliceRemoveAll([]bool{false, true, false}, true), []bool{false, false})
}

View file

@ -107,11 +107,11 @@ func toAPIHook(ctx *context.APIContext, repoLink string, hook *webhook.Webhook)
} }
func issuesHook(events []string, event string) bool { func issuesHook(events []string, event string) bool {
return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(webhook_module.HookEventIssues), events, true) return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventIssues), true)
} }
func pullHook(events []string, event string) bool { func pullHook(events []string, event string) bool {
return util.IsStringInSlice(event, events, true) || util.IsStringInSlice(string(webhook_module.HookEventPullRequest), events, true) return util.SliceContainsString(events, event, true) || util.SliceContainsString(events, string(webhook_module.HookEventPullRequest), true)
} }
// addHook add the hook specified by `form`, `orgID` and `repoID`. If there is // addHook add the hook specified by `form`, `orgID` and `repoID`. If there is
@ -130,15 +130,15 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
HookEvent: &webhook_module.HookEvent{ HookEvent: &webhook_module.HookEvent{
ChooseEvents: true, ChooseEvents: true,
HookEvents: webhook_module.HookEvents{ HookEvents: webhook_module.HookEvents{
Create: util.IsStringInSlice(string(webhook_module.HookEventCreate), form.Events, true), Create: util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true),
Delete: util.IsStringInSlice(string(webhook_module.HookEventDelete), form.Events, true), Delete: util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true),
Fork: util.IsStringInSlice(string(webhook_module.HookEventFork), form.Events, true), Fork: util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true),
Issues: issuesHook(form.Events, "issues_only"), Issues: issuesHook(form.Events, "issues_only"),
IssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)), IssueAssign: issuesHook(form.Events, string(webhook_module.HookEventIssueAssign)),
IssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)), IssueLabel: issuesHook(form.Events, string(webhook_module.HookEventIssueLabel)),
IssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)), IssueMilestone: issuesHook(form.Events, string(webhook_module.HookEventIssueMilestone)),
IssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)), IssueComment: issuesHook(form.Events, string(webhook_module.HookEventIssueComment)),
Push: util.IsStringInSlice(string(webhook_module.HookEventPush), form.Events, true), Push: util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true),
PullRequest: pullHook(form.Events, "pull_request_only"), PullRequest: pullHook(form.Events, "pull_request_only"),
PullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)), PullRequestAssign: pullHook(form.Events, string(webhook_module.HookEventPullRequestAssign)),
PullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)), PullRequestLabel: pullHook(form.Events, string(webhook_module.HookEventPullRequestLabel)),
@ -146,9 +146,9 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
PullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)), PullRequestComment: pullHook(form.Events, string(webhook_module.HookEventPullRequestComment)),
PullRequestReview: pullHook(form.Events, "pull_request_review"), PullRequestReview: pullHook(form.Events, "pull_request_review"),
PullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)), PullRequestSync: pullHook(form.Events, string(webhook_module.HookEventPullRequestSync)),
Wiki: util.IsStringInSlice(string(webhook_module.HookEventWiki), form.Events, true), Wiki: util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true),
Repository: util.IsStringInSlice(string(webhook_module.HookEventRepository), form.Events, true), Repository: util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true),
Release: util.IsStringInSlice(string(webhook_module.HookEventRelease), form.Events, true), Release: util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true),
}, },
BranchFilter: form.BranchFilter, BranchFilter: form.BranchFilter,
}, },
@ -277,14 +277,14 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
w.PushOnly = false w.PushOnly = false
w.SendEverything = false w.SendEverything = false
w.ChooseEvents = true w.ChooseEvents = true
w.Create = util.IsStringInSlice(string(webhook_module.HookEventCreate), form.Events, true) w.Create = util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true)
w.Push = util.IsStringInSlice(string(webhook_module.HookEventPush), form.Events, true) w.Push = util.SliceContainsString(form.Events, string(webhook_module.HookEventPush), true)
w.Create = util.IsStringInSlice(string(webhook_module.HookEventCreate), form.Events, true) w.Create = util.SliceContainsString(form.Events, string(webhook_module.HookEventCreate), true)
w.Delete = util.IsStringInSlice(string(webhook_module.HookEventDelete), form.Events, true) w.Delete = util.SliceContainsString(form.Events, string(webhook_module.HookEventDelete), true)
w.Fork = util.IsStringInSlice(string(webhook_module.HookEventFork), form.Events, true) w.Fork = util.SliceContainsString(form.Events, string(webhook_module.HookEventFork), true)
w.Repository = util.IsStringInSlice(string(webhook_module.HookEventRepository), form.Events, true) w.Repository = util.SliceContainsString(form.Events, string(webhook_module.HookEventRepository), true)
w.Wiki = util.IsStringInSlice(string(webhook_module.HookEventWiki), form.Events, true) w.Wiki = util.SliceContainsString(form.Events, string(webhook_module.HookEventWiki), true)
w.Release = util.IsStringInSlice(string(webhook_module.HookEventRelease), form.Events, true) w.Release = util.SliceContainsString(form.Events, string(webhook_module.HookEventRelease), true)
w.BranchFilter = form.BranchFilter w.BranchFilter = form.BranchFilter
err := w.SetHeaderAuthorization(form.AuthorizationHeader) err := w.SetHeaderAuthorization(form.AuthorizationHeader)

View file

@ -139,7 +139,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
viewType := ctx.FormString("type") viewType := ctx.FormString("type")
sortType := ctx.FormString("sort") sortType := ctx.FormString("sort")
types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned", "review_requested"} types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned", "review_requested"}
if !util.IsStringInSlice(viewType, types, true) { if !util.SliceContainsString(types, viewType, true) {
viewType = "all" viewType = "all"
} }
@ -3087,7 +3087,7 @@ func updateAttachments(ctx *context.Context, item interface{}, files []string) e
return fmt.Errorf("unknown Type: %T", content) return fmt.Errorf("unknown Type: %T", content)
} }
for i := 0; i < len(attachments); i++ { for i := 0; i < len(attachments); i++ {
if util.IsStringInSlice(attachments[i].UUID, files) { if util.SliceContainsString(files, attachments[i].UUID) {
continue continue
} }
if err := repo_model.DeleteAttachment(attachments[i], true); err != nil { if err := repo_model.DeleteAttachment(attachments[i], true); err != nil {

View file

@ -110,7 +110,7 @@ func getOrgRepoCtx(ctx *context.Context) (*orgRepoCtx, error) {
func checkHookType(ctx *context.Context) string { func checkHookType(ctx *context.Context) string {
hookType := strings.ToLower(ctx.Params(":type")) hookType := strings.ToLower(ctx.Params(":type"))
if !util.IsStringInSlice(hookType, setting.Webhook.Types, true) { if !util.SliceContainsString(setting.Webhook.Types, hookType, true) {
ctx.NotFound("checkHookType", nil) ctx.NotFound("checkHookType", nil)
return "" return ""
} }

View file

@ -214,7 +214,7 @@ func NotificationSubscriptions(ctx *context.Context) {
ctx.Data["SortType"] = sortType ctx.Data["SortType"] = sortType
state := ctx.FormString("state") state := ctx.FormString("state")
if !util.IsStringInSlice(state, []string{"all", "open", "closed"}, true) { if !util.SliceContainsString([]string{"all", "open", "closed"}, state, true) {
state = "all" state = "all"
} }
ctx.Data["State"] = state ctx.Data["State"] = state

View file

@ -413,7 +413,7 @@ func UpdateUserLang(ctx *context.Context) {
ctx.Data["PageIsSettingsAppearance"] = true ctx.Data["PageIsSettingsAppearance"] = true
if len(form.Language) != 0 { if len(form.Language) != 0 {
if !util.IsStringInSlice(form.Language, setting.Langs) { if !util.SliceContainsString(setting.Langs, form.Language) {
ctx.Flash.Error(ctx.Tr("settings.update_language_not_found", form.Language)) ctx.Flash.Error(ctx.Tr("settings.update_language_not_found", form.Language))
ctx.Redirect(setting.AppSubURL + "/user/settings/appearance") ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
return return

View file

@ -246,7 +246,7 @@ func (source *Source) getMappedMemberships(l *ldap.Conn, uid string) (map[string
membershipsToAdd := map[string][]string{} membershipsToAdd := map[string][]string{}
membershipsToRemove := map[string][]string{} membershipsToRemove := map[string][]string{}
for group, memberships := range ldapGroupsToTeams { for group, memberships := range ldapGroupsToTeams {
isUserInGroup := util.IsStringInSlice(group, usersLdapGroups) isUserInGroup := util.SliceContainsString(usersLdapGroups, group)
if isUserInGroup { if isUserInGroup {
for org, teams := range memberships { for org, teams := range memberships {
membershipsToAdd[org] = teams membershipsToAdd[org] = teams

View file

@ -23,7 +23,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
idx := strings.Index(userName, "@") idx := strings.Index(userName, "@")
if idx == -1 { if idx == -1 {
return nil, user_model.ErrUserNotExist{Name: userName} return nil, user_model.ErrUserNotExist{Name: userName}
} else if !util.IsStringInSlice(userName[idx+1:], strings.Split(source.AllowedDomains, ","), true) { } else if !util.SliceContainsString(strings.Split(source.AllowedDomains, ","), userName[idx+1:], true) {
return nil, user_model.ErrUserNotExist{Name: userName} return nil, user_model.ErrUserNotExist{Name: userName}
} }
} }

View file

@ -35,7 +35,7 @@ const (
) )
func nameAllowed(name string) error { func nameAllowed(name string) error {
if util.IsStringInSlice(name, reservedWikiNames) { if util.SliceContainsString(reservedWikiNames, name) {
return repo_model.ErrWikiReservedName{ return repo_model.ErrWikiReservedName{
Title: name, Title: name,
} }

View file

@ -38,7 +38,7 @@ func TestAPIRepoTeams(t *testing.T) {
if assert.Len(t, teams, 2) { if assert.Len(t, teams, 2) {
assert.EqualValues(t, "Owners", teams[0].Name) assert.EqualValues(t, "Owners", teams[0].Name)
assert.True(t, teams[0].CanCreateOrgRepo) assert.True(t, teams[0].CanCreateOrgRepo)
assert.True(t, util.IsEqualSlice(unit.AllUnitKeyNames(), teams[0].Units), fmt.Sprintf("%v == %v", unit.AllUnitKeyNames(), teams[0].Units)) assert.True(t, util.SliceSortedEqual(unit.AllUnitKeyNames(), teams[0].Units), fmt.Sprintf("%v == %v", unit.AllUnitKeyNames(), teams[0].Units))
assert.EqualValues(t, "owner", teams[0].Permission) assert.EqualValues(t, "owner", teams[0].Permission)
assert.EqualValues(t, "test_team", teams[1].Name) assert.EqualValues(t, "test_team", teams[1].Name)