Skip to content

Commit

Permalink
Add functionality to slicesext (#2619)
Browse files Browse the repository at this point in the history
- Rename `ToMap` to `ToStructMap`.
- Add `ToValuesMap` that maps `[]V` to a `map[K]V` based on a
`func(V)K`. Think `moduleFullNameToModuleRef :=
slicesext.ToValuesMap(moduleRefs, func(moduleRef ModuleRef) string {
return moduleRef.ModuleFullName().String })`
- Add `Duplicates` to return duplicates in a slice. To be used in
validation.
  • Loading branch information
bufdev authored Nov 22, 2023
1 parent 1e7722c commit 7f54239
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 26 deletions.
4 changes: 2 additions & 2 deletions private/buf/bufwire/module_config_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ func (m *moduleConfigReader) getProtoFileModuleSourceConfigSet(
if err != nil {
return nil, err
}
workspaceConfigs := slicesext.ToMap(bufwork.AllConfigFilePaths)
moduleConfigs := slicesext.ToMap(bufconfig.AllConfigFilePaths)
workspaceConfigs := slicesext.ToStructMap(bufwork.AllConfigFilePaths)
moduleConfigs := slicesext.ToStructMap(bufconfig.AllConfigFilePaths)
terminateFileProvider := readBucketCloser.TerminateFileProvider()
var workspaceConfigDirectory string
var moduleConfigDirectory string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func run(
container.Logger(),
flags.Origin,
clientTLSConfig,
slicesext.ToMap(flags.DisallowedHeaders),
slicesext.ToStructMap(flags.DisallowedHeaders),
flags.ForwardHeaders,
flags.PrivateNetwork,
)
Expand Down
4 changes: 2 additions & 2 deletions private/bufpkg/bufimage/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func imageWithOnlyPaths(image Image, fileOrDirPaths []string, excludeFileOrDirPa
if err := normalpath.ValidatePathsNormalizedValidatedUnique(excludeFileOrDirPaths); err != nil {
return nil, err
}
excludeFileOrDirPathMap := slicesext.ToMap(excludeFileOrDirPaths)
excludeFileOrDirPathMap := slicesext.ToStructMap(excludeFileOrDirPaths)
// These are the files that fileOrDirPaths actually reference and will
// result in the non-imports in our resulting Image. The Image will also include
// the ImageFiles that the nonImportImageFiles import
Expand Down Expand Up @@ -132,7 +132,7 @@ func imageWithOnlyPaths(image Image, fileOrDirPaths []string, excludeFileOrDirPa
// make a map of the directory paths
// note that we do not make this a map to begin with as maps are unordered,
// and we want to make sure we iterate over the paths in a deterministic order
potentialDirPathMap := slicesext.ToMap(potentialDirPaths)
potentialDirPathMap := slicesext.ToStructMap(potentialDirPaths)

// map of all paths based on the imageFiles
// the map of paths within potentialDirPath that matches a file in image.Files()
Expand Down
2 changes: 1 addition & 1 deletion private/bufpkg/bufmodule/bufmoduleconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func newConfigV1Beta1(externalConfig ExternalConfigV1Beta1, deps ...string) (*Co
}

// verify that all excludes are within a root
rootMap := slicesext.ToMap(roots)
rootMap := slicesext.ToStructMap(roots)
for _, fullExclude := range fullExcludes {
switch matchingRoots := normalpath.MapAllEqualOrContainingPaths(rootMap, fullExclude, normalpath.Relative); len(matchingRoots) {
case 0:
Expand Down
4 changes: 2 additions & 2 deletions private/bufpkg/bufmodule/targeting_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (m *targetingModule) TargetFileInfos(ctx context.Context) (fileInfos []bufm
bufmoduleref.SortFileInfos(fileInfos)
}
}()
excludePathMap := slicesext.ToMap(m.excludePaths)
excludePathMap := slicesext.ToStructMap(m.excludePaths)
// We start by ensuring that no paths have been duplicated between target and exclude pathes.
for _, targetPath := range m.targetPaths {
if _, ok := excludePathMap[targetPath]; ok {
Expand Down Expand Up @@ -150,7 +150,7 @@ func (m *targetingModule) TargetFileInfos(ctx context.Context) (fileInfos []bufm
}
// We have potential directory paths, do the expensive operation to
// make a map of the directory paths.
potentialDirPathMap := slicesext.ToMap(potentialDirPaths)
potentialDirPathMap := slicesext.ToStructMap(potentialDirPaths)
// The map of paths within potentialDirPath that matches a file.
// This needs to contain all paths in potentialDirPathMap at the end for us to
// have had matches for every targetPath input.
Expand Down
4 changes: 2 additions & 2 deletions private/pkg/bandeps/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func (s *state) packagesForPackageExpressionUncached(
span.SetStatus(codes.Error, err.Error())
return nil, err
}
return slicesext.ToMap(getNonEmptyLines(string(data))), nil
return slicesext.ToStructMap(getNonEmptyLines(string(data))), nil
}

func (s *state) depsForPackage(
Expand Down Expand Up @@ -234,7 +234,7 @@ func (s *state) depsForPackageUncached(
span.SetStatus(codes.Error, err.Error())
return nil, err
}
return slicesext.ToMap(getNonEmptyLines(string(data))), nil
return slicesext.ToStructMap(getNonEmptyLines(string(data))), nil
}

type packagesResult struct {
Expand Down
2 changes: 1 addition & 1 deletion private/pkg/git/lister.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (l *lister) ListFilesAndUnstagedFiles(

// stringSliceExcept returns all elements in source that are not in except.
func stringSliceExcept(source []string, except []string) []string {
exceptMap := slicesext.ToMap(except)
exceptMap := slicesext.ToStructMap(except)
result := make([]string, 0, len(source))
for _, s := range source {
if _, ok := exceptMap[s]; !ok {
Expand Down
8 changes: 4 additions & 4 deletions private/pkg/normalpath/normalpath_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ func TestMapHasEqualOrContainingPath(t *testing.T) {
}

func testMapHasEqualOrContainingPath(t *testing.T, expected bool, path string, keys ...string) {
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapHasEqualOrContainingPath(keyMap, path, Relative), fmt.Sprintf("%s %v", path, keys))
}

Expand All @@ -367,7 +367,7 @@ func testMapAllEqualOrContainingPaths(t *testing.T, expected []string, path stri
expected = make([]string, 0)
}
sort.Strings(expected)
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapAllEqualOrContainingPaths(keyMap, path, Relative), fmt.Sprintf("%s %v", path, keys))
}

Expand Down Expand Up @@ -424,7 +424,7 @@ func TestMapHasEqualOrContainingPathAbs(t *testing.T) {
}

func testMapHasEqualOrContainingPathAbs(t *testing.T, expected bool, path string, keys ...string) {
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapHasEqualOrContainingPath(keyMap, path, Absolute), fmt.Sprintf("%s %v", path, keys))
}

Expand All @@ -449,6 +449,6 @@ func testMapAllEqualOrContainingPathsAbs(t *testing.T, expected []string, path s
expected = make([]string, 0)
}
sort.Strings(expected)
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapAllEqualOrContainingPaths(keyMap, path, Absolute), fmt.Sprintf("%s %v", path, keys))
}
8 changes: 4 additions & 4 deletions private/pkg/normalpath/normalpath_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ func TestMapHasEqualOrContainingPath(t *testing.T) {
}

func testMapHasEqualOrContainingPath(t *testing.T, expected bool, path string, keys ...string) {
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapHasEqualOrContainingPath(keyMap, path, Relative), fmt.Sprintf("%s %v", path, keys))
}

Expand All @@ -386,7 +386,7 @@ func testMapAllEqualOrContainingPaths(t *testing.T, expected []string, path stri
expected = make([]string, 0)
}
sort.Strings(expected)
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapAllEqualOrContainingPaths(keyMap, path, Relative), fmt.Sprintf("%s %v", path, keys))
}

Expand Down Expand Up @@ -497,7 +497,7 @@ func TestMapHasEqualOrContainingPathAbs(t *testing.T) {
}

func testMapHasEqualOrContainingPathAbs(t *testing.T, expected bool, path string, keys ...string) {
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapHasEqualOrContainingPath(keyMap, path, Absolute), fmt.Sprintf("%s %v", path, keys))
}

Expand Down Expand Up @@ -527,6 +527,6 @@ func testMapAllEqualOrContainingPathsAbs(t *testing.T, expected []string, path s
expected = make([]string, 0)
}
sort.Strings(expected)
keyMap := slicesext.ToMap(keys)
keyMap := slicesext.ToStructMap(keys)
assert.Equal(t, expected, MapAllEqualOrContainingPaths(keyMap, path, Absolute), fmt.Sprintf("%s %v", path, keys))
}
36 changes: 32 additions & 4 deletions private/pkg/slicesext/slicesext.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,26 @@ func Copy[T any](s []T) []T {
return sc
}

// ToMap converts the slice to a map.
func ToMap[T comparable](s []T) map[T]struct{} {
// ToStructMap converts the slice to a map with struct{} values.
func ToStructMap[T comparable](s []T) map[T]struct{} {
m := make(map[T]struct{}, len(s))
for _, e := range s {
m[e] = struct{}{}
}
return m
}

// ToValuesMap transforms the input slice into a map from f(V) -> V.
//
// Duplicate values of type K will result in a single map entry.
func ToValuesMapV[K comparable, V any](s []V, f func(V) K) map[K]V {
m := make(map[K]V)
for _, e := range s {
m[f(e)] = e
}
return m
}

// MapKeysToSortedSlice converts the map's keys to a sorted slice.
func MapKeysToSortedSlice[M ~map[K]V, K Ordered, V any](m M) []K {
s := MapKeysToSlice(m)
Expand All @@ -171,7 +182,24 @@ func MapKeysToSlice[K comparable, V any](m map[K]V) []K {

// ToUniqueSorted returns a sorted copy of s with no duplicates.
func ToUniqueSorted[S ~[]T, T Ordered](s S) S {
return MapKeysToSortedSlice(ToMap(s))
return MapKeysToSortedSlice(ToStructMap(s))
}

// Duplicates returns the duplicate values in s.
//
// Values are returned in the order they are found in S.
func Duplicates[T comparable](s []T) []T {
count := make(map[T]int, len(s))
// Needed instead of var declaration to make tests pass.
duplicates := make([]T, 0)
for _, e := range s {
count[e] = count[e] + 1
if count[e] == 2 {
// Only insert the first time this is found.
duplicates = append(duplicates, e)
}
}
return duplicates
}

// ToChunks splits s into chunks of the given chunk size.
Expand Down Expand Up @@ -214,7 +242,7 @@ func ElementsEqual[T comparable](one []T, two []T) bool {
//
// Nil and empty slices are treated as equals.
func ElementsContained(superset []string, subset []string) bool {
m := ToMap(superset)
m := ToStructMap(superset)
for _, elem := range subset {
if _, ok := m[elem]; !ok {
return false
Expand Down
29 changes: 29 additions & 0 deletions private/pkg/slicesext/slicesext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,35 @@ func TestElementsContained(t *testing.T) {
assert.False(t, ElementsContained([]string{"two"}, []string{"one", "two"}))
}

func TestDuplicates(t *testing.T) {
t.Parallel()
assert.Equal(
t,
[]string{},
Duplicates([]string{"a", "b", "c", "d", "e"}),
)
assert.Equal(
t,
[]string{"a"},
Duplicates([]string{"a", "b", "c", "a", "e"}),
)
assert.Equal(
t,
[]string{"a"},
Duplicates([]string{"a", "a", "c", "a", "e"}),
)
assert.Equal(
t,
[]string{"b", "a"},
Duplicates([]string{"a", "b", "b", "a", "e"}),
)
assert.Equal(
t,
[]string{"b", "a"},
Duplicates([]string{"a", "b", "b", "a", "b"}),
)
}

func TestToChunks(t *testing.T) {
t.Parallel()
testToChunks(
Expand Down
6 changes: 3 additions & 3 deletions private/pkg/stringutil/stringutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ func MapToSlice(m map[string]struct{}) []string {

// SliceToMap transforms s to a map.
//
// Deprecated: Use slicesext.ToMap instead.
// Deprecated: Use slicesext.ToStructMap instead.
func SliceToMap(s []string) map[string]struct{} {
return slicesext.ToMap(s)
return slicesext.ToStructMap(s)
}

// SliceToUniqueSortedSlice returns a sorted copy of s with no duplicates.
Expand All @@ -88,7 +88,7 @@ func SliceToUniqueSortedSlice(s []string) []string {
//
// Strings with only spaces are considered empty.
func SliceToUniqueSortedSliceFilterEmptyStrings(s []string) []string {
m := slicesext.ToMap(s)
m := slicesext.ToStructMap(s)
for key := range m {
if strings.TrimSpace(key) == "" {
delete(m, key)
Expand Down

0 comments on commit 7f54239

Please sign in to comment.