chore: `IsSubset` was moved to `x/exp/slice` (#923)

Andrey Nering created

Change summary

go.mod                            |   2 
go.sum                            |   4 
internal/shell/shell.go           |   4 
internal/slicesext/slices.go      |  17 ---
internal/slicesext/slices_test.go | 158 ---------------------------------
5 files changed, 5 insertions(+), 180 deletions(-)

Detailed changes

go.mod 🔗

@@ -76,7 +76,7 @@ require (
 	github.com/charmbracelet/colorprofile v0.3.2 // indirect
 	github.com/charmbracelet/ultraviolet v0.0.0-20250813213450-50737e162af5
 	github.com/charmbracelet/x/cellbuf v0.0.14-0.20250811133356-e0c5dbe5ea4a // indirect
-	github.com/charmbracelet/x/exp/slice v0.0.0-20250611152503-f53cdd7e01ef
+	github.com/charmbracelet/x/exp/slice v0.0.0-20250829135019-44e44e21330d
 	github.com/charmbracelet/x/term v0.2.1
 	github.com/charmbracelet/x/termios v0.1.1 // indirect
 	github.com/charmbracelet/x/windows v0.2.2 // indirect

go.sum 🔗

@@ -102,8 +102,8 @@ github.com/charmbracelet/x/exp/charmtone v0.0.0-20250708181618-a60a724ba6c3 h1:1
 github.com/charmbracelet/x/exp/charmtone v0.0.0-20250708181618-a60a724ba6c3/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
 github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
 github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
-github.com/charmbracelet/x/exp/slice v0.0.0-20250611152503-f53cdd7e01ef h1:v7qwsZ2OxzlwvpKwz8dtZXp7fIJlcDEUOyFBNE4fz4Q=
-github.com/charmbracelet/x/exp/slice v0.0.0-20250611152503-f53cdd7e01ef/go.mod h1:vI5nDVMWi6veaYH+0Fmvpbe/+cv/iJfMntdh+N0+Tms=
+github.com/charmbracelet/x/exp/slice v0.0.0-20250829135019-44e44e21330d h1:H2oh4WlSsXy8qwLd7I3eAvPd/X3S40aM9l+h47WF1eA=
+github.com/charmbracelet/x/exp/slice v0.0.0-20250829135019-44e44e21330d/go.mod h1:vI5nDVMWi6veaYH+0Fmvpbe/+cv/iJfMntdh+N0+Tms=
 github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
 github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
 github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=

internal/shell/shell.go 🔗

@@ -20,7 +20,7 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/charmbracelet/crush/internal/slicesext"
+	"github.com/charmbracelet/x/exp/slice"
 	"mvdan.cc/sh/moreinterp/coreutils"
 	"mvdan.cc/sh/v3/expand"
 	"mvdan.cc/sh/v3/interp"
@@ -186,7 +186,7 @@ func ArgumentsBlocker(cmd string, args []string, flags []string) BlockFunc {
 		}
 
 		argsMatch := slices.Equal(argParts[:len(args)], args)
-		flagsMatch := slicesext.IsSubset(flags, flagParts)
+		flagsMatch := slice.IsSubset(flags, flagParts)
 
 		return argsMatch && flagsMatch
 	}

internal/slicesext/slices.go 🔗

@@ -1,17 +0,0 @@
-package slicesext
-
-func IsSubset[T comparable](a, b []T) bool {
-	if len(a) > len(b) {
-		return false
-	}
-	set := make(map[T]struct{}, len(b))
-	for _, item := range b {
-		set[item] = struct{}{}
-	}
-	for _, item := range a {
-		if _, exists := set[item]; !exists {
-			return false
-		}
-	}
-	return true
-}

internal/slicesext/slices_test.go 🔗

@@ -1,158 +0,0 @@
-package slicesext
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestIsSubset(t *testing.T) {
-	tests := []struct {
-		name   string
-		a      []string
-		b      []string
-		expect bool
-	}{
-		// Basic subset cases
-		{
-			name:   "empty subset of empty",
-			a:      []string{},
-			b:      []string{},
-			expect: true,
-		},
-		{
-			name:   "empty subset of non-empty",
-			a:      []string{},
-			b:      []string{"a", "b", "c"},
-			expect: true,
-		},
-		{
-			name:   "non-empty not subset of empty",
-			a:      []string{"a"},
-			b:      []string{},
-			expect: false,
-		},
-		{
-			name:   "single element subset",
-			a:      []string{"b"},
-			b:      []string{"a", "b", "c"},
-			expect: true,
-		},
-		{
-			name:   "single element not subset",
-			a:      []string{"d"},
-			b:      []string{"a", "b", "c"},
-			expect: false,
-		},
-		{
-			name:   "multiple elements subset",
-			a:      []string{"a", "c"},
-			b:      []string{"a", "b", "c", "d"},
-			expect: true,
-		},
-		{
-			name:   "multiple elements not subset",
-			a:      []string{"a", "e"},
-			b:      []string{"a", "b", "c", "d"},
-			expect: false,
-		},
-		{
-			name:   "equal sets are subsets",
-			a:      []string{"a", "b", "c"},
-			b:      []string{"a", "b", "c"},
-			expect: true,
-		},
-		{
-			name:   "larger set not subset of smaller",
-			a:      []string{"a", "b", "c", "d"},
-			b:      []string{"a", "b"},
-			expect: false,
-		},
-
-		// Order independence
-		{
-			name:   "subset with different order",
-			a:      []string{"c", "a"},
-			b:      []string{"b", "a", "d", "c"},
-			expect: true,
-		},
-
-		// Duplicate handling
-		{
-			name:   "duplicates in subset",
-			a:      []string{"a", "a", "b"},
-			b:      []string{"a", "b", "c"},
-			expect: true,
-		},
-		{
-			name:   "duplicates in superset",
-			a:      []string{"a", "b"},
-			b:      []string{"a", "a", "b", "b", "c"},
-			expect: true,
-		},
-		{
-			name:   "duplicates in both",
-			a:      []string{"a", "a", "b"},
-			b:      []string{"a", "a", "b", "b", "c"},
-			expect: true,
-		},
-
-		// Real-world examples
-		{
-			name:   "npm flags subset",
-			a:      []string{"-g"},
-			b:      []string{"-g", "--verbose", "--save-dev"},
-			expect: true,
-		},
-		{
-			name:   "npm flags not subset",
-			a:      []string{"--global"},
-			b:      []string{"-g", "--verbose", "--save-dev"},
-			expect: false,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := IsSubset(tt.a, tt.b)
-			require.Equal(t, tt.expect, result,
-				"IsSubset(%v, %v) should be %v", tt.a, tt.b, tt.expect)
-		})
-	}
-}
-
-func TestIsSubsetWithInts(t *testing.T) {
-	tests := []struct {
-		name   string
-		a      []int
-		b      []int
-		expect bool
-	}{
-		{
-			name:   "int subset",
-			a:      []int{1, 3},
-			b:      []int{1, 2, 3, 4},
-			expect: true,
-		},
-		{
-			name:   "int not subset",
-			a:      []int{1, 5},
-			b:      []int{1, 2, 3, 4},
-			expect: false,
-		},
-		{
-			name:   "empty int subset",
-			a:      []int{},
-			b:      []int{1, 2, 3},
-			expect: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := IsSubset(tt.a, tt.b)
-			require.Equal(t, tt.expect, result,
-				"IsSubset(%v, %v) should be %v", tt.a, tt.b, tt.expect)
-		})
-	}
-}