add hyprland module subcommands

parent 86e75277
......@@ -6,7 +6,9 @@ import (
)
type HyprEnv struct {
Config string
Config string
SystemModulesDir string
UserModulesDir string
}
type Environment struct {
......@@ -28,7 +30,21 @@ func InitConfig() error {
home, _ := os.UserHomeDir()
if Env.Hyprland != nil {
confPath := filepath.Join(home, ".config", "hypr", "hyprland.conf")
userModulesPath := filepath.Join(home, ".config", "hypr")
systemModulesPath := "/etc/ximperdistro/hyprland/hypr"
confPath := filepath.Join(userModulesPath, "hyprland.conf")
if _, err := os.Stat(userModulesPath); os.IsNotExist(err) {
_ = os.MkdirAll(userModulesPath, 0755)
}
Env.Hyprland.UserModulesDir = userModulesPath
if _, err := os.Stat(systemModulesPath); os.IsNotExist(err) {
Env.Hyprland.SystemModulesDir = ""
} else {
Env.Hyprland.SystemModulesDir = systemModulesPath
}
if _, err := os.Stat(confPath); err == nil {
Env.Hyprland.Config = confPath
} else {
......
......@@ -2,4 +2,13 @@ module ximperconf
go 1.25.0
require github.com/urfave/cli/v3 v3.4.1
require (
github.com/fatih/color v1.18.0
github.com/urfave/cli/v3 v3.4.1
)
require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.25.0 // indirect
)
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM=
github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package hyprland
import (
"context"
"fmt"
"ximperconf/config"
"bufio"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/fatih/color"
"github.com/urfave/cli/v3"
)
var (
HomeDir, _ = os.UserHomeDir()
)
func CheckHyprland(ctx context.Context, cmd *cli.Command) error {
h := config.Env.Hyprland
if h == nil || h.Config == "" {
......@@ -39,3 +48,190 @@ func CheckHyprland(ctx context.Context, cmd *cli.Command) error {
fmt.Println(strings.Join(result, "\n"))
return nil
}
func hyprlandGetModuleFile(module string, user bool) string {
if user {
return filepath.Join(config.Env.Hyprland.UserModulesDir, module+".conf")
}
return filepath.Join(config.Env.Hyprland.SystemModulesDir, module+".conf")
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func hyprlandModuleStatus(module string, user bool) string {
sysFile := hyprlandGetModuleFile(module, user)
lineFull := "source = " + sysFile
lineTilde := "source = ~" + strings.TrimPrefix(sysFile, HomeDir)
if !fileExists(sysFile) {
fmt.Printf("Модуль '%s' не найден: %s", module, sysFile)
os.Exit(1)
return ""
}
if !fileExists(config.Env.Hyprland.Config) {
return "disabled"
}
f, err := os.Open(config.Env.Hyprland.Config)
if err != nil {
return "disabled"
}
defer f.Close()
reFull := regexp.MustCompile(`^\s*` + regexp.QuoteMeta(lineFull) + `(?:\s|$)`)
reTilde := regexp.MustCompile(`^\s*` + regexp.QuoteMeta(lineTilde) + `(?:\s|$)`)
sc := bufio.NewScanner(f)
for sc.Scan() {
line := sc.Text()
if reFull.MatchString(line) || reTilde.MatchString(line) {
return "enabled"
}
}
return "disabled"
}
func HyprlandModuleStatusCommand(ctx context.Context, cmd *cli.Command) error {
status := hyprlandModuleStatus(cmd.Args().Get(0), cmd.Bool("user"))
fmt.Println(status)
return nil
}
func hyprlandSetModule(action string, module string, user bool) {
sysFile := hyprlandGetModuleFile(module, user)
lineFull := "source = " + sysFile
lineTilde := "source = ~" + strings.TrimPrefix(sysFile, HomeDir)
if !fileExists(sysFile) {
fmt.Printf("Модуль '%s' не найден: %s", module, sysFile)
return
}
data, _ := os.ReadFile(config.Env.Hyprland.Config)
lines := strings.Split(string(data), "\n")
changed := false
switch action {
case "enable":
for i, l := range lines {
if strings.TrimSpace(l) == lineFull || strings.TrimSpace(l) == lineTilde {
fmt.Printf("Модуль '%s' уже включён", module)
return
}
re := regexp.MustCompile(`^\s*#\s*` + regexp.QuoteMeta(lineFull) + `$`)
if re.MatchString(l) {
lines[i] = lineFull
changed = true
}
re = regexp.MustCompile(`^\s*#\s*` + regexp.QuoteMeta(lineTilde) + `$`)
if re.MatchString(l) {
lines[i] = lineFull
changed = true
}
}
if !changed {
lines = append(lines, lineTilde)
}
color.Green("Модуль '%s' включён", module)
case "disable":
for i, l := range lines {
reFull := regexp.MustCompile(`^\s*#\s*` + regexp.QuoteMeta(lineFull) + `$`)
reTilde := regexp.MustCompile(`^\s*#\s*` + regexp.QuoteMeta(lineTilde) + `$`)
if reFull.MatchString(l) || reTilde.MatchString(l) {
color.Yellow("Модуль '%s' уже отключён", module)
return
}
if strings.TrimSpace(l) == lineFull || strings.TrimSpace(l) == lineTilde {
lines[i] = "# " + lineFull
changed = true
}
}
if !changed {
color.Red("Модуль '%s' не найден в конфигурации", module)
return
}
color.Yellow("Модуль '%s' отключён", module)
}
_ = os.WriteFile(config.Env.Hyprland.Config, []byte(strings.Join(lines, "\n")), 0644)
}
func HyprlandModuleEnableCommand(ctx context.Context, cmd *cli.Command) error {
hyprlandSetModule("enable", cmd.Args().Get(0), cmd.Bool("user"))
return nil
}
func HyprlandModuleDisableCommand(ctx context.Context, cmd *cli.Command) error {
hyprlandSetModule("disable", cmd.Args().Get(0), cmd.Bool("user"))
return nil
}
func HyprlandToggleModuleCommand(ctx context.Context, cmd *cli.Command) error {
module := cmd.Args().Get(0)
user := cmd.Bool("user")
status := hyprlandModuleStatus(module, user)
switch status {
case "enabled":
hyprlandSetModule("disable", module, user)
case "disabled":
hyprlandSetModule("enable", module, user)
}
return nil
}
func hyprlandListModules(user bool, filter string) {
var dir string
if user {
dir = config.Env.Hyprland.UserModulesDir
} else {
dir = config.Env.Hyprland.SystemModulesDir
}
entries, err := os.ReadDir(dir)
if err != nil {
fmt.Printf("Каталог с модулями не найден: %s", dir)
return
}
skip := map[string]bool{
"hyprland": true, "hypridle": true,
"hyprlock": true, "hyprpaper": true,
"hyprsunset": true, "xdph": true,
}
for _, e := range entries {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".conf") {
continue
}
module := strings.TrimSuffix(e.Name(), ".conf")
if skip[module] {
continue
}
status := hyprlandModuleStatus(module, user)
if filter == "enabled" && status != "enabled" {
continue
}
if filter == "disabled" && status != "disabled" {
continue
}
switch status {
case "enabled":
color.Green(module)
case "disabled":
color.Yellow(module)
default:
color.Red(module)
}
}
}
func HyprlandListModulesCommand(ctx context.Context, cmd *cli.Command) error {
hyprlandListModules(cmd.Bool("user"), "all")
return nil
}
......@@ -17,6 +17,49 @@ func CommandList() *cli.Command {
Usage: "Check the Hyprland config",
Action: CheckHyprland,
},
{
Name: "module",
Usage: "Hyprland modules",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "user",
Usage: "use user modules",
Aliases: []string{"u"},
Value: false,
},
},
Commands: []*cli.Command{
{
Name: "status",
Usage: "Hyprland module status",
ArgsUsage: "module",
Action: HyprlandModuleStatusCommand,
},
{
Name: "list",
Usage: "list modules",
Action: HyprlandListModulesCommand,
},
{
Name: "enable",
Usage: "enable module",
ArgsUsage: "module",
Action: HyprlandModuleEnableCommand,
},
{
Name: "disable",
Usage: "disable module",
ArgsUsage: "module",
Action: HyprlandModuleDisableCommand,
},
{
Name: "toggle",
Usage: "toggle module",
ArgsUsage: "module",
Action: HyprlandToggleModuleCommand,
},
},
},
},
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment