diff options
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils')
12 files changed, 899 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/dbus.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/dbus.go new file mode 100644 index 0000000..bafd51f --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/dbus.go @@ -0,0 +1,37 @@ +package utils + +import ( + "slices" + + "github.com/godbus/dbus/v5" +) + +func IsDBusServiceAvailable(busName string) bool { + conn, err := dbus.ConnectSystemBus() + if err != nil { + return false + } + defer conn.Close() + + obj := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") + var owned bool + if err := obj.Call("org.freedesktop.DBus.NameHasOwner", 0, busName).Store(&owned); err != nil { + return false + } + return owned +} + +func IsDBusServiceActivatable(busName string) bool { + conn, err := dbus.ConnectSystemBus() + if err != nil { + return false + } + defer conn.Close() + + obj := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") + var activatable []string + if err := obj.Call("org.freedesktop.DBus.ListActivatableNames", 0).Store(&activatable); err != nil { + return false + } + return slices.Contains(activatable, busName) +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/exec.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/exec.go new file mode 100644 index 0000000..aa1eb31 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/exec.go @@ -0,0 +1,44 @@ +package utils + +import ( + "os/exec" +) + +type AppChecker interface { + CommandExists(cmd string) bool + AnyCommandExists(cmds ...string) bool + FlatpakExists(name string) bool + AnyFlatpakExists(flatpaks ...string) bool +} + +type DefaultAppChecker struct{} + +func (DefaultAppChecker) CommandExists(cmd string) bool { + return CommandExists(cmd) +} + +func (DefaultAppChecker) AnyCommandExists(cmds ...string) bool { + return AnyCommandExists(cmds...) +} + +func (DefaultAppChecker) FlatpakExists(name string) bool { + return FlatpakExists(name) +} + +func (DefaultAppChecker) AnyFlatpakExists(flatpaks ...string) bool { + return AnyFlatpakExists(flatpaks...) +} + +func CommandExists(cmd string) bool { + _, err := exec.LookPath(cmd) + return err == nil +} + +func AnyCommandExists(cmds ...string) bool { + for _, cmd := range cmds { + if CommandExists(cmd) { + return true + } + } + return false +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak.go new file mode 100644 index 0000000..f1c9009 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak.go @@ -0,0 +1,83 @@ +package utils + +import ( + "bytes" + "errors" + "os/exec" + "slices" + "strings" +) + +func FlatpakInPath() bool { + _, err := exec.LookPath("flatpak") + return err == nil +} + +func FlatpakExists(name string) bool { + if !FlatpakInPath() { + return false + } + + cmd := exec.Command("flatpak", "info", name) + err := cmd.Run() + return err == nil +} + +func FlatpakSearchBySubstring(substring string) bool { + if !FlatpakInPath() { + return false + } + + cmd := exec.Command("flatpak", "list", "--app") + var stdout bytes.Buffer + cmd.Stdout = &stdout + + if err := cmd.Run(); err != nil { + return false + } + + out := stdout.String() + + for line := range strings.SplitSeq(out, "\n") { + fields := strings.Fields(line) + if len(fields) > 1 { + id := fields[1] + idParts := strings.Split(id, ".") + // We are assuming that the last part of the ID is + // the package name we're looking for. This might + // not always be true, some developers use arbitrary + // suffixes. + if len(idParts) > 0 && idParts[len(idParts)-1] == substring { + cmd := exec.Command("flatpak", "info", id) + err := cmd.Run() + return err == nil + } + } + } + return false +} + +func AnyFlatpakExists(flatpaks ...string) bool { + return slices.ContainsFunc(flatpaks, FlatpakExists) +} + +func FlatpakInstallationDir(name string) (string, error) { + if !FlatpakInPath() { + return "", errors.New("flatpak not found in PATH") + } + + cmd := exec.Command("flatpak", "info", "--show-location", name) + var stdout bytes.Buffer + cmd.Stdout = &stdout + + if err := cmd.Run(); err != nil { + return "", errors.New("flatpak not installed: " + name) + } + + location := strings.TrimSpace(stdout.String()) + if location == "" { + return "", errors.New("installation directory not found for: " + name) + } + + return location, nil +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak_test.go new file mode 100644 index 0000000..97282ae --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/flatpak_test.go @@ -0,0 +1,271 @@ +package utils + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestFlatpakInPathAvailable(t *testing.T) { + result := FlatpakInPath() + if !result { + t.Skip("flatpak not in PATH") + } + if !result { + t.Errorf("expected true when flatpak is in PATH") + } +} + +func TestFlatpakInPathUnavailable(t *testing.T) { + tempDir := t.TempDir() + t.Setenv("PATH", tempDir) + + result := FlatpakInPath() + if result { + t.Errorf("expected false when flatpak not in PATH, got true") + } +} + +func TestFlatpakExistsValidPackage(t *testing.T) { + if !FlatpakInPath() { + t.Skip("flatpak not in PATH") + } + + result := FlatpakExists("com.nonexistent.package.test") + if result { + t.Logf("package exists (unexpected but not an error)") + } +} + +func TestFlatpakExistsNoFlatpak(t *testing.T) { + tempDir := t.TempDir() + t.Setenv("PATH", tempDir) + + result := FlatpakExists("any.package.name") + if result { + t.Errorf("expected false when flatpak not in PATH, got true") + } +} + +func TestFlatpakSearchBySubstringNoFlatpak(t *testing.T) { + tempDir := t.TempDir() + t.Setenv("PATH", tempDir) + + result := FlatpakSearchBySubstring("test") + if result { + t.Errorf("expected false when flatpak not in PATH, got true") + } +} + +func TestFlatpakSearchBySubstringNonexistent(t *testing.T) { + if !FlatpakInPath() { + t.Skip("flatpak not in PATH") + } + + result := FlatpakSearchBySubstring("ThisIsAVeryUnlikelyPackageName12345") + if result { + t.Errorf("expected false for nonexistent package substring") + } +} + +func TestFlatpakInstallationDirNoFlatpak(t *testing.T) { + tempDir := t.TempDir() + t.Setenv("PATH", tempDir) + + _, err := FlatpakInstallationDir("any.package.name") + if err == nil { + t.Errorf("expected error when flatpak not in PATH") + } + if err != nil && !strings.Contains(err.Error(), "not found in PATH") { + t.Errorf("expected 'not found in PATH' error, got: %v", err) + } +} + +func TestFlatpakInstallationDirNonexistent(t *testing.T) { + if !FlatpakInPath() { + t.Skip("flatpak not in PATH") + } + + _, err := FlatpakInstallationDir("com.nonexistent.package.test") + if err == nil { + t.Errorf("expected error for nonexistent package") + } + if err != nil && !strings.Contains(err.Error(), "not installed") { + t.Errorf("expected 'not installed' error, got: %v", err) + } +} + +func TestFlatpakInstallationDirValid(t *testing.T) { + if !FlatpakInPath() { + t.Skip("flatpak not in PATH") + } + + // This test requires a known installed flatpak + // We can't guarantee any specific flatpak is installed, + // so we'll skip if we can't find a common one + commonFlatpaks := []string{ + "org.mozilla.firefox", + "org.gnome.Calculator", + "org.freedesktop.Platform", + } + + var testPackage string + for _, pkg := range commonFlatpaks { + if FlatpakExists(pkg) { + testPackage = pkg + break + } + } + + if testPackage == "" { + t.Skip("no common flatpak packages found for testing") + } + + result, err := FlatpakInstallationDir(testPackage) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result == "" { + t.Errorf("expected non-empty installation directory") + } + if !strings.Contains(result, testPackage) { + t.Logf("installation directory %s doesn't contain package name (may be expected)", result) + } +} + +func TestFlatpakExistsCommandFailure(t *testing.T) { + if !FlatpakInPath() { + t.Skip("flatpak not in PATH") + } + + // Mock a failing flatpak command through PATH interception + tempDir := t.TempDir() + fakeFlatpak := filepath.Join(tempDir, "flatpak") + + script := "#!/bin/sh\nexit 1\n" + err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) + if err != nil { + t.Fatalf("failed to create fake flatpak: %v", err) + } + + originalPath := os.Getenv("PATH") + t.Setenv("PATH", tempDir+":"+originalPath) + + result := FlatpakExists("test.package") + if result { + t.Errorf("expected false when flatpak command fails, got true") + } +} + +func TestFlatpakSearchBySubstringCommandFailure(t *testing.T) { + if !FlatpakInPath() { + t.Skip("flatpak not in PATH") + } + + // Mock a failing flatpak command through PATH interception + tempDir := t.TempDir() + fakeFlatpak := filepath.Join(tempDir, "flatpak") + + script := "#!/bin/sh\nexit 1\n" + err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) + if err != nil { + t.Fatalf("failed to create fake flatpak: %v", err) + } + + originalPath := os.Getenv("PATH") + t.Setenv("PATH", tempDir+":"+originalPath) + + result := FlatpakSearchBySubstring("test") + if result { + t.Errorf("expected false when flatpak command fails, got true") + } +} + +func TestFlatpakInstallationDirCommandFailure(t *testing.T) { + if !FlatpakInPath() { + t.Skip("flatpak not in PATH") + } + + // Mock a failing flatpak command through PATH interception + tempDir := t.TempDir() + fakeFlatpak := filepath.Join(tempDir, "flatpak") + + script := "#!/bin/sh\nexit 1\n" + err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) + if err != nil { + t.Fatalf("failed to create fake flatpak: %v", err) + } + + originalPath := os.Getenv("PATH") + t.Setenv("PATH", tempDir+":"+originalPath) + + _, err = FlatpakInstallationDir("test.package") + if err == nil { + t.Errorf("expected error when flatpak command fails") + } + if err != nil && !strings.Contains(err.Error(), "not installed") { + t.Errorf("expected 'not installed' error, got: %v", err) + } +} + +func TestAnyFlatpakExistsSomeExist(t *testing.T) { + tempDir := t.TempDir() + fakeFlatpak := filepath.Join(tempDir, "flatpak") + + // Script that succeeds only for "app.exists.test" + script := `#!/bin/sh +if [ "$1" = "info" ] && [ "$2" = "app.exists.test" ]; then + exit 0 +fi +exit 1 +` + err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) + if err != nil { + t.Fatalf("failed to create fake flatpak: %v", err) + } + + originalPath := os.Getenv("PATH") + t.Setenv("PATH", tempDir+":"+originalPath) + + result := AnyFlatpakExists("com.nonexistent.flatpak", "app.exists.test", "com.another.nonexistent") + if !result { + t.Errorf("expected true when at least one flatpak exists") + } +} + +func TestAnyFlatpakExistsNoneExist(t *testing.T) { + tempDir := t.TempDir() + fakeFlatpak := filepath.Join(tempDir, "flatpak") + + script := "#!/bin/sh\nexit 1\n" + err := os.WriteFile(fakeFlatpak, []byte(script), 0o755) + if err != nil { + t.Fatalf("failed to create fake flatpak: %v", err) + } + + originalPath := os.Getenv("PATH") + t.Setenv("PATH", tempDir+":"+originalPath) + + result := AnyFlatpakExists("com.nonexistent.flatpak1", "com.nonexistent.flatpak2") + if result { + t.Errorf("expected false when no flatpaks exist") + } +} + +func TestAnyFlatpakExistsNoFlatpak(t *testing.T) { + tempDir := t.TempDir() + t.Setenv("PATH", tempDir) + + result := AnyFlatpakExists("any.package.name", "another.package") + if result { + t.Errorf("expected false when flatpak not in PATH, got true") + } +} + +func TestAnyFlatpakExistsEmpty(t *testing.T) { + result := AnyFlatpakExists() + if result { + t.Errorf("expected false when no flatpaks specified") + } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group.go new file mode 100644 index 0000000..1a0a950 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group.go @@ -0,0 +1,37 @@ +package utils + +import ( + "os" + "strings" +) + +func HasGroup(groupName string) bool { + return HasGroupIn(groupName, "/etc/group") +} + +func HasGroupIn(groupName, path string) bool { + data, err := os.ReadFile(path) + if err != nil { + return false + } + return HasGroupData(groupName, string(data)) +} + +func HasGroupData(groupName, data string) bool { + prefix := groupName + ":" + for line := range strings.SplitSeq(data, "\n") { + if strings.HasPrefix(line, prefix) { + return true + } + } + return false +} + +func FindGroupData(data string, candidates ...string) (string, bool) { + for _, candidate := range candidates { + if HasGroupData(candidate, data) { + return candidate, true + } + } + return "", false +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group_test.go new file mode 100644 index 0000000..4b401dc --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/group_test.go @@ -0,0 +1,142 @@ +package utils + +import "testing" + +const testGroupData = `root:x:0:brltty,root +sys:x:3:bin,testuser +mem:x:8: +ftp:x:11: +mail:x:12: +log:x:19: +smmsp:x:25: +proc:x:26: +games:x:50: +lock:x:54: +network:x:90: +floppy:x:94: +scanner:x:96: +power:x:98: +nobody:x:65534: +adm:x:999:daemon +wheel:x:998:testuser +utmp:x:997: +audio:x:996:brltty +disk:x:995: +input:x:994:brltty,testuser,greeter +kmem:x:993: +kvm:x:992:libvirt-qemu,qemu,testuser +lp:x:991:cups,testuser +optical:x:990: +render:x:989: +sgx:x:988: +storage:x:987: +tty:x:5:brltty +uucp:x:986:brltty +video:x:985:cosmic-greeter,greeter,testuser +users:x:984: +groups:x:983: +systemd-journal:x:982: +rfkill:x:981: +bin:x:1:daemon +daemon:x:2:bin +http:x:33: +dbus:x:81: +systemd-coredump:x:980: +systemd-network:x:979: +systemd-oom:x:978: +systemd-journal-remote:x:977: +systemd-resolve:x:976: +systemd-timesync:x:975: +tss:x:974: +uuidd:x:973: +alpm:x:972: +polkitd:x:102: +testuser:x:1000: +avahi:x:971: +git:x:970: +nvidia-persistenced:x:143: +i2c:x:969:testuser +seat:x:968: +rtkit:x:133: +brlapi:x:967:brltty +gdm:x:120: +brltty:x:966: +colord:x:965: +flatpak:x:964: +geoclue:x:963:testuser +gnome-remote-desktop:x:962: +saned:x:961: +usbmux:x:140: +cosmic-greeter:x:960: +greeter:x:959:testuser +openvpn:x:958: +nm-openvpn:x:957: +named:x:40: +_talkd:x:956: +keyd:x:955: +cups:x:209:testuser +docker:x:954:testuser +mysql:x:953: +radicale:x:952: +onepassword:x:1001: +nixbld:x:951:nixbld01,nixbld02,nixbld03,nixbld04,nixbld05,nixbld06,nixbld07,nixbld08,nixbld09,nixbld10 +virtlogin:x:940: +libvirt:x:939:testuser +libvirt-qemu:x:938: +qemu:x:937: +dnsmasq:x:936: +clock:x:935: +dms-greeter:x:1002:greeter,testuser +pcscd:x:934: +test:x:1003: +empower:x:933: +` + +func TestHasGroupData(t *testing.T) { + tests := []struct { + group string + want bool + }{ + {"greeter", true}, + {"root", true}, + {"docker", true}, + {"cosmic-greeter", true}, + {"dms-greeter", true}, + {"nonexistent", false}, + {"greet", false}, + } + + for _, tt := range tests { + if got := HasGroupData(tt.group, testGroupData); got != tt.want { + t.Errorf("HasGroupData(%q) = %v, want %v", tt.group, got, tt.want) + } + } +} + +func TestFindGroupData(t *testing.T) { + tests := []struct { + name string + candidates []string + wantGroup string + wantFound bool + }{ + {"first match wins", []string{"greeter", "greetd", "_greeter"}, "greeter", true}, + {"fallback to second", []string{"greetd", "greeter"}, "greeter", true}, + {"none found", []string{"_greetd", "greetd"}, "", false}, + {"single match", []string{"docker"}, "docker", true}, + } + + for _, tt := range tests { + got, found := FindGroupData(testGroupData, tt.candidates...) + if got != tt.wantGroup || found != tt.wantFound { + t.Errorf("%s: FindGroupData(%v) = (%q, %v), want (%q, %v)", + tt.name, tt.candidates, got, found, tt.wantGroup, tt.wantFound) + } + } +} + +func TestHasGroupDataEmpty(t *testing.T) { + if HasGroupData("greeter", "") { + t.Error("expected false for empty data") + } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/gsettings.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/gsettings.go new file mode 100644 index 0000000..cc8d084 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/gsettings.go @@ -0,0 +1,31 @@ +package utils + +import ( + "fmt" + "os/exec" + "strings" +) + +func dconfPath(schema, key string) string { + return "/" + strings.ReplaceAll(schema, ".", "/") + "/" + key +} + +// GsettingsGet reads a gsettings value, falling back to dconf read. +func GsettingsGet(schema, key string) (string, error) { + if out, err := exec.Command("gsettings", "get", schema, key).Output(); err == nil { + return strings.TrimSpace(string(out)), nil + } + out, err := exec.Command("dconf", "read", dconfPath(schema, key)).Output() + if err != nil { + return "", fmt.Errorf("gsettings/dconf get failed for %s %s: %w", schema, key, err) + } + return strings.TrimSpace(string(out)), nil +} + +// GsettingsSet writes a gsettings value, falling back to dconf write. +func GsettingsSet(schema, key, value string) error { + if err := exec.Command("gsettings", "set", schema, key, value).Run(); err == nil { + return nil + } + return exec.Command("dconf", "write", dconfPath(schema, key), "'"+value+"'").Run() +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/math.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/math.go new file mode 100644 index 0000000..408af9f --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/math.go @@ -0,0 +1,13 @@ +package utils + +import "golang.org/x/exp/constraints" + +func Clamp[T constraints.Ordered](val, min, max T) T { + if val < min { + return min + } + if val > max { + return max + } + return val +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths.go new file mode 100644 index 0000000..053097e --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths.go @@ -0,0 +1,70 @@ +package utils + +import ( + "os" + "path/filepath" + "strings" +) + +func XDGStateHome() string { + if dir := os.Getenv("XDG_STATE_HOME"); dir != "" { + return dir + } + home, _ := os.UserHomeDir() + return filepath.Join(home, ".local", "state") +} + +func XDGDataHome() string { + if dir := os.Getenv("XDG_DATA_HOME"); dir != "" { + return dir + } + home, _ := os.UserHomeDir() + return filepath.Join(home, ".local", "share") +} + +func XDGCacheHome() string { + if dir, err := os.UserCacheDir(); err == nil { + return dir + } + home, _ := os.UserHomeDir() + return filepath.Join(home, ".cache") +} + +func XDGConfigHome() string { + if dir, err := os.UserConfigDir(); err == nil { + return dir + } + home, _ := os.UserHomeDir() + return filepath.Join(home, ".config") +} + +func EmacsConfigDir() string { + home, _ := os.UserHomeDir() + + emacsD := filepath.Join(home, ".emacs.d") + if info, err := os.Stat(emacsD); err == nil && info.IsDir() { + return emacsD + } + + xdgEmacs := filepath.Join(XDGConfigHome(), "emacs") + if info, err := os.Stat(xdgEmacs); err == nil && info.IsDir() { + return xdgEmacs + } + + return "" +} + +func ExpandPath(path string) (string, error) { + expanded := os.ExpandEnv(path) + expanded = filepath.Clean(expanded) + + if strings.HasPrefix(expanded, "~") { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + expanded = filepath.Join(home, expanded[1:]) + } + + return expanded, nil +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths_test.go new file mode 100644 index 0000000..964f2a7 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/paths_test.go @@ -0,0 +1,43 @@ +package utils + +import ( + "os" + "path/filepath" + "testing" +) + +func TestExpandPathTilde(t *testing.T) { + home, err := os.UserHomeDir() + if err != nil { + t.Skip("no home directory") + } + result, err := ExpandPath("~/test") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + expected := filepath.Join(home, "test") + if result != expected { + t.Errorf("expected %s, got %s", expected, result) + } +} + +func TestExpandPathEnvVar(t *testing.T) { + t.Setenv("TEST_PATH_VAR", "/custom/path") + result, err := ExpandPath("$TEST_PATH_VAR/subdir") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result != "/custom/path/subdir" { + t.Errorf("expected /custom/path/subdir, got %s", result) + } +} + +func TestExpandPathAbsolute(t *testing.T) { + result, err := ExpandPath("/absolute/path") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result != "/absolute/path" { + t.Errorf("expected /absolute/path, got %s", result) + } +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices.go new file mode 100644 index 0000000..b636ddc --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices.go @@ -0,0 +1,56 @@ +package utils + +func Filter[T any](items []T, predicate func(T) bool) []T { + var result []T + for _, item := range items { + if predicate(item) { + result = append(result, item) + } + } + return result +} + +func Find[T any](items []T, predicate func(T) bool) (T, bool) { + for _, item := range items { + if predicate(item) { + return item, true + } + } + var zero T + return zero, false +} + +func Map[T, U any](items []T, transform func(T) U) []U { + result := make([]U, len(items)) + for i, item := range items { + result[i] = transform(item) + } + return result +} + +func Contains[T comparable](items []T, target T) bool { + for _, item := range items { + if item == target { + return true + } + } + return false +} + +func Any[T any](items []T, predicate func(T) bool) bool { + for _, item := range items { + if predicate(item) { + return true + } + } + return false +} + +func All[T any](items []T, predicate func(T) bool) bool { + for _, item := range items { + if !predicate(item) { + return false + } + } + return true +} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices_test.go new file mode 100644 index 0000000..8edcee5 --- /dev/null +++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/utils/slices_test.go @@ -0,0 +1,72 @@ +package utils + +import ( + "testing" +) + +func TestFilter(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + evens := Filter(nums, func(n int) bool { return n%2 == 0 }) + if len(evens) != 2 || evens[0] != 2 || evens[1] != 4 { + t.Errorf("expected [2, 4], got %v", evens) + } +} + +func TestFilterEmpty(t *testing.T) { + result := Filter([]int{1, 2, 3}, func(n int) bool { return n > 10 }) + if len(result) != 0 { + t.Errorf("expected empty slice, got %v", result) + } +} + +func TestFind(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + val, found := Find(nums, func(n int) bool { return n == 3 }) + if !found || val != 3 { + t.Errorf("expected 3, got %v (found=%v)", val, found) + } +} + +func TestFindNotFound(t *testing.T) { + nums := []int{1, 2, 3} + val, found := Find(nums, func(n int) bool { return n == 99 }) + if found || val != 0 { + t.Errorf("expected zero value not found, got %v (found=%v)", val, found) + } +} + +func TestMap(t *testing.T) { + nums := []int{1, 2, 3} + doubled := Map(nums, func(n int) int { return n * 2 }) + if len(doubled) != 3 || doubled[0] != 2 || doubled[1] != 4 || doubled[2] != 6 { + t.Errorf("expected [2, 4, 6], got %v", doubled) + } +} + +func TestMapTypeConversion(t *testing.T) { + nums := []int{1, 2, 3} + strs := Map(nums, func(n int) string { return string(rune('a' + n - 1)) }) + if strs[0] != "a" || strs[1] != "b" || strs[2] != "c" { + t.Errorf("expected [a, b, c], got %v", strs) + } +} + +func TestContains(t *testing.T) { + nums := []int{1, 2, 3} + if !Contains(nums, 2) { + t.Error("expected to contain 2") + } + if Contains(nums, 99) { + t.Error("expected not to contain 99") + } +} + +func TestAny(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + if !Any(nums, func(n int) bool { return n > 4 }) { + t.Error("expected any > 4") + } + if Any(nums, func(n int) bool { return n > 10 }) { + t.Error("expected none > 10") + } +} |