create: update config struct

parent a751baae
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
) )
type CreateEnv struct { type CreateEnv struct {
Config string ConfigDir string
} }
type HyprEnv struct { type HyprEnv struct {
...@@ -36,7 +36,7 @@ func InitConfig() error { ...@@ -36,7 +36,7 @@ func InitConfig() error {
Env.IsRoot = os.Geteuid() == 0 Env.IsRoot = os.Geteuid() == 0
Env.Create = &CreateEnv{ Env.Create = &CreateEnv{
Config: "/etc/ximperdistro/ximperconf/profiles.yaml", ConfigDir: "/etc/ximperdistro/ximperconf/create.d/",
} }
if utils.FileExists("/usr/bin/hyprland") { if utils.FileExists("/usr/bin/hyprland") {
......
...@@ -106,8 +106,10 @@ func processProfile(prof Profile, dryRun bool, res *Result) { ...@@ -106,8 +106,10 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
} }
} }
// ----- hyprvars ----- // ----- hyprland -----
for _, v := range prof.HyprVars {
// --------- vars
for _, v := range prof.Hyprland.Vars {
_, err := hyprland.HyprlandVarGet(v.Name) _, err := hyprland.HyprlandVarGet(v.Name)
if err == nil { if err == nil {
res.HyprVars = append(res.HyprVars, res.HyprVars = append(res.HyprVars,
...@@ -129,8 +131,8 @@ func processProfile(prof Profile, dryRun bool, res *Result) { ...@@ -129,8 +131,8 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
} }
// ----- hyprmodules ----- // --------- modules
for _, m := range prof.HyprModules { for _, m := range prof.Hyprland.Modules {
if m.Module == "" { if m.Module == "" {
res.HyprModules = append(res.HyprModules, "Пропущен модуль без имени") res.HyprModules = append(res.HyprModules, "Пропущен модуль без имени")
continue continue
...@@ -170,8 +172,8 @@ func processProfile(prof Profile, dryRun bool, res *Result) { ...@@ -170,8 +172,8 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
} }
} }
// ----- синхронизация раскладок Hyprland----- // --------- синхронизация раскладок Hyprland
if prof.SyncSystemLayouts && !dryRun { if prof.Hyprland.Option.SyncSystemLayouts && !dryRun {
sysLayouts, err := hyprland.HyprlandGetKeyboardLayouts() sysLayouts, err := hyprland.HyprlandGetKeyboardLayouts()
if err != nil { if err != nil {
res.SyncSystemLayouts = append(res.SyncSystemLayouts, res.SyncSystemLayouts = append(res.SyncSystemLayouts,
...@@ -210,18 +212,41 @@ func processProfile(prof Profile, dryRun bool, res *Result) { ...@@ -210,18 +212,41 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
} }
} }
func loadConfig(path string) (*Config, error) { func loadConfig(dir string) (*Config, error) {
data, err := os.ReadFile(path) cfg := &Config{Profiles: map[string]Profile{}}
entries, err := os.ReadDir(dir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var cfg Config for _, e := range entries {
if err := yaml.Unmarshal(data, &cfg); err != nil { if e.IsDir() {
return nil, err continue
}
name := e.Name()
if !strings.HasSuffix(name, ".yaml") && !strings.HasSuffix(name, ".yml") {
continue
}
path := filepath.Join(dir, name)
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read %s: %w", name, err)
}
var p Profile
if err := yaml.Unmarshal(data, &p); err != nil {
return nil, fmt.Errorf("parse %s: %w", name, err)
}
profileName := strings.TrimSuffix(name, filepath.Ext(name))
cfg.Profiles[profileName] = p
} }
return &cfg, nil return cfg, nil
} }
func expandPath(path, name string) string { func expandPath(path, name string) string {
...@@ -403,30 +428,108 @@ func printTree(title string, lines []string) { ...@@ -403,30 +428,108 @@ func printTree(title string, lines []string) {
fmt.Println() fmt.Println()
} }
func createProfile(profile string, dryRun bool, res *Result) error { func createProfile(profileName string, prof Profile, dryRun bool, res *Result) {
cfg, err := loadConfig(config.Env.Create.Config) color.Green("Создаётся профиль: %s", profileName)
processProfile(prof, dryRun, res)
}
func ShowProfilesInfo() {
cfg, err := loadConfig(config.Env.Create.ConfigDir)
if err != nil { if err != nil {
color.Red("Не удалось загрузить конфигурацию: %v", err) color.Red("Не удалось загрузить конфигурацию: %v", err)
return err return
} }
prof, ok := cfg.Profiles[profile] if len(cfg.Profiles) == 0 {
if !ok { color.Red("Нет доступных профилей")
color.Red("Профиль %s не найден", profile) return
return nil
} }
color.Green("Создаётся профиль: %s", profile) color.Blue("Profiles:")
processProfile(prof, dryRun, res)
return nil statusMap := map[bool]struct {
symbol string
color func(format string, a ...interface{}) string
}{
true: {symbol: "●", color: color.GreenString},
false: {symbol: "○", color: color.RedString},
}
i := 0
for name, profile := range cfg.Profiles {
available := ProfileAvailable(profile)
info := statusMap[available]
desc := profile.Description
if desc == "" {
desc = "-"
}
prefix := "├──"
if i == len(cfg.Profiles)-1 {
prefix = "└──"
}
coloredStatus := info.color(info.symbol)
coloredName := info.color(name)
fmt.Printf("%s %s %s — %s\n", prefix, coloredStatus, coloredName, desc)
i++
}
} }
func CreateConfCommand(ctx context.Context, cmd *cli.Command) error { func CreateConfCommand(ctx context.Context, cmd *cli.Command) error {
profile := cmd.Args().Get(0) profile := cmd.Args().Get(0)
auto := cmd.Bool("auto")
if profile == "" && !auto {
ShowProfilesInfo()
return nil
}
if auto {
var err error
profile, err = AutoDetectProfile()
if err != nil {
color.Red("Не удалось определить профиль: %v", err)
return err
}
color.Green("Автоматически выбран профиль: %s", profile)
}
dryRun := cmd.Bool("dry-run") dryRun := cmd.Bool("dry-run")
res := &Result{}
createProfile(profile, dryRun, res)
cfg, err := loadConfig(config.Env.Create.ConfigDir)
if err != nil {
color.Red("Не удалось загрузить конфигурацию: %v", err)
return err
}
mainProf, ok := cfg.Profiles[profile]
if !ok {
color.Red("Профиль %s не найден", profile)
return fmt.Errorf("профиль %s не найден", profile)
}
for _, depName := range mainProf.Depends {
depProf, ok := cfg.Profiles[depName]
if !ok {
color.Yellow("Зависимость %s не найдена, пропускаем", depName)
continue
}
res := &Result{}
createProfile(depName, depProf, dryRun, res)
printTree("Копируем", res.Copies)
printTree("Создаём ссылки", res.Links)
printTree("Обновляем", res.Replaced)
printTree("Создаём переменные Hyprland", res.HyprVars)
printTree("Подключаем модули Hyprland", res.HyprModules)
printTree("Синхронизируем раскладку Hyprland", res.SyncSystemLayouts)
}
res := &Result{}
createProfile(profile, mainProf, dryRun, res)
printTree("Копируем", res.Copies) printTree("Копируем", res.Copies)
printTree("Создаём ссылки", res.Links) printTree("Создаём ссылки", res.Links)
printTree("Обновляем", res.Replaced) printTree("Обновляем", res.Replaced)
......
...@@ -3,7 +3,6 @@ package create ...@@ -3,7 +3,6 @@ package create
import ( import (
"context" "context"
"fmt" "fmt"
"os/exec"
"ximperconf/config" "ximperconf/config"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
...@@ -20,6 +19,12 @@ func CommandList() *cli.Command { ...@@ -20,6 +19,12 @@ func CommandList() *cli.Command {
Aliases: []string{"d"}, Aliases: []string{"d"},
Value: false, Value: false,
}, },
&cli.BoolFlag{
Name: "auto",
Usage: " Automatic profile selection",
Aliases: []string{"a"},
Value: false,
},
}, },
Action: CreateConfCommand, Action: CreateConfCommand,
ShellComplete: ShellCompleteProfiles, ShellComplete: ShellCompleteProfiles,
...@@ -31,21 +36,13 @@ func ShellCompleteProfiles(ctx context.Context, cmd *cli.Command) { ...@@ -31,21 +36,13 @@ func ShellCompleteProfiles(ctx context.Context, cmd *cli.Command) {
return return
} }
cfg, err := loadConfig(config.Env.Create.Config) cfg, err := loadConfig(config.Env.Create.ConfigDir)
if err != nil { if err != nil {
return return
} }
for profileName, profile := range cfg.Profiles { for profileName, profile := range cfg.Profiles {
show := true if ProfileAvailable(profile) {
if profile.Binary != "" {
if _, err := exec.LookPath(profile.Binary); err != nil {
show = false
}
}
if show {
fmt.Println(profileName) fmt.Println(profileName)
} }
} }
......
...@@ -29,16 +29,27 @@ type HyprModule struct { ...@@ -29,16 +29,27 @@ type HyprModule struct {
IfExec string `yaml:"if-exec,omitempty"` IfExec string `yaml:"if-exec,omitempty"`
} }
type HyprOptions struct {
SyncSystemLayouts bool `yaml:"sync-system-layouts,omitempty"`
}
type HyprlandEntry struct {
Option HyprOptions `yaml:"options,omitempty"`
Vars []HyprVar `yaml:"rvars,omitempty"`
Modules []HyprModule `yaml:"modules,omitempty"`
}
type Profile struct { type Profile struct {
Binary string `yaml:"binary,omitempty"` Binary string `yaml:"binary,omitempty"`
Description string `yaml:"description,omitempty"`
Root bool `yaml:"root,omitempty"`
Depends []string `yaml:"depends,omitempty"`
Copy []CopyEntry `yaml:"copy,omitempty"` Copy []CopyEntry `yaml:"copy,omitempty"`
Links []LinkEntry `yaml:"links"` Links []LinkEntry `yaml:"links,omitempty"`
ReleaseReplace []ReleaseReplaceEntry `yaml:"release-replace,omitempty"` ReleaseReplace []ReleaseReplaceEntry `yaml:"release-replace,omitempty"`
// ----- hyprland ----- // ----- hyprland -----
SyncSystemLayouts bool `yaml:"sync-system-layouts,omitempty"` Hyprland HyprlandEntry `yaml:"hyprland,omitempty"`
HyprVars []HyprVar `yaml:"hyprvars,omitempty"`
HyprModules []HyprModule `yaml:"hyprmodules,omitempty"`
} }
type Config struct { type Config struct {
......
package create
import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"
"ximperconf/config"
"github.com/fatih/color"
)
func ProfileAvailable(p Profile) bool {
allowed := false
if p.Binary == "" {
allowed = true
} else if _, err := exec.LookPath(p.Binary); err == nil {
allowed = true
}
if p.Root != config.Env.IsRoot {
allowed = false
}
return allowed
}
func AutoDetectProfile() (string, error) {
cfg, err := loadConfig(config.Env.Create.ConfigDir)
if err != nil {
color.Red("Не удалось загрузить конфигурацию: %v", err)
return "", err
}
for name, profile := range cfg.Profiles {
if profile.Binary == "" || !ProfileAvailable(profile) {
continue
}
cmd := exec.Command("pgrep", "-u", fmt.Sprintf("%d", os.Getuid()), "-x", profile.Binary)
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
continue
}
if strings.TrimSpace(out.String()) != "" {
return name, nil
}
}
return "", fmt.Errorf("не найден запущенный профиль")
}
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