create: update config struct

parent a751baae
......@@ -7,7 +7,7 @@ import (
)
type CreateEnv struct {
Config string
ConfigDir string
}
type HyprEnv struct {
......@@ -36,7 +36,7 @@ func InitConfig() error {
Env.IsRoot = os.Geteuid() == 0
Env.Create = &CreateEnv{
Config: "/etc/ximperdistro/ximperconf/profiles.yaml",
ConfigDir: "/etc/ximperdistro/ximperconf/create.d/",
}
if utils.FileExists("/usr/bin/hyprland") {
......
......@@ -106,8 +106,10 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
}
}
// ----- hyprvars -----
for _, v := range prof.HyprVars {
// ----- hyprland -----
// --------- vars
for _, v := range prof.Hyprland.Vars {
_, err := hyprland.HyprlandVarGet(v.Name)
if err == nil {
res.HyprVars = append(res.HyprVars,
......@@ -129,8 +131,8 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
}
// ----- hyprmodules -----
for _, m := range prof.HyprModules {
// --------- modules
for _, m := range prof.Hyprland.Modules {
if m.Module == "" {
res.HyprModules = append(res.HyprModules, "Пропущен модуль без имени")
continue
......@@ -170,8 +172,8 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
}
}
// ----- синхронизация раскладок Hyprland-----
if prof.SyncSystemLayouts && !dryRun {
// --------- синхронизация раскладок Hyprland
if prof.Hyprland.Option.SyncSystemLayouts && !dryRun {
sysLayouts, err := hyprland.HyprlandGetKeyboardLayouts()
if err != nil {
res.SyncSystemLayouts = append(res.SyncSystemLayouts,
......@@ -210,18 +212,41 @@ func processProfile(prof Profile, dryRun bool, res *Result) {
}
}
func loadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
func loadConfig(dir string) (*Config, error) {
cfg := &Config{Profiles: map[string]Profile{}}
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, err
for _, e := range entries {
if e.IsDir() {
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 {
......@@ -403,30 +428,108 @@ func printTree(title string, lines []string) {
fmt.Println()
}
func createProfile(profile string, dryRun bool, res *Result) error {
cfg, err := loadConfig(config.Env.Create.Config)
func createProfile(profileName string, prof Profile, dryRun bool, res *Result) {
color.Green("Создаётся профиль: %s", profileName)
processProfile(prof, dryRun, res)
}
func ShowProfilesInfo() {
cfg, err := loadConfig(config.Env.Create.ConfigDir)
if err != nil {
color.Red("Не удалось загрузить конфигурацию: %v", err)
return err
return
}
prof, ok := cfg.Profiles[profile]
if !ok {
color.Red("Профиль %s не найден", profile)
return nil
if len(cfg.Profiles) == 0 {
color.Red("Нет доступных профилей")
return
}
color.Green("Создаётся профиль: %s", profile)
processProfile(prof, dryRun, res)
return nil
color.Blue("Profiles:")
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 {
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")
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.Links)
printTree("Обновляем", res.Replaced)
......
......@@ -3,7 +3,6 @@ package create
import (
"context"
"fmt"
"os/exec"
"ximperconf/config"
"github.com/urfave/cli/v3"
......@@ -20,6 +19,12 @@ func CommandList() *cli.Command {
Aliases: []string{"d"},
Value: false,
},
&cli.BoolFlag{
Name: "auto",
Usage: " Automatic profile selection",
Aliases: []string{"a"},
Value: false,
},
},
Action: CreateConfCommand,
ShellComplete: ShellCompleteProfiles,
......@@ -31,21 +36,13 @@ func ShellCompleteProfiles(ctx context.Context, cmd *cli.Command) {
return
}
cfg, err := loadConfig(config.Env.Create.Config)
cfg, err := loadConfig(config.Env.Create.ConfigDir)
if err != nil {
return
}
for profileName, profile := range cfg.Profiles {
show := true
if profile.Binary != "" {
if _, err := exec.LookPath(profile.Binary); err != nil {
show = false
}
}
if show {
if ProfileAvailable(profile) {
fmt.Println(profileName)
}
}
......
......@@ -29,16 +29,27 @@ type HyprModule struct {
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 {
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"`
Links []LinkEntry `yaml:"links"`
Links []LinkEntry `yaml:"links,omitempty"`
ReleaseReplace []ReleaseReplaceEntry `yaml:"release-replace,omitempty"`
// ----- hyprland -----
SyncSystemLayouts bool `yaml:"sync-system-layouts,omitempty"`
HyprVars []HyprVar `yaml:"hyprvars,omitempty"`
HyprModules []HyprModule `yaml:"hyprmodules,omitempty"`
Hyprland HyprlandEntry `yaml:"hyprland,omitempty"`
}
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