Commit e64e7d14 authored by Kirill Unitsaev's avatar Kirill Unitsaev

preset: integrate status indicators into result output

parent 6eeca1f8
...@@ -22,7 +22,7 @@ var ( ...@@ -22,7 +22,7 @@ var (
validActions = []string{"enable", "disable", "remove"} validActions = []string{"enable", "disable", "remove"}
) )
func processCopies(prof config.PresetProfile, opts opOptions, res *config.PresetResult) { func processCopies(prof config.PresetProfile, opts opOptions, res *Result) {
for _, c := range prof.Copy { for _, c := range prof.Copy {
src := expandPath(c.Src, "") src := expandPath(c.Src, "")
dest := expandPath(c.Dest, "") dest := expandPath(c.Dest, "")
...@@ -31,7 +31,7 @@ func processCopies(prof config.PresetProfile, opts opOptions, res *config.Preset ...@@ -31,7 +31,7 @@ func processCopies(prof config.PresetProfile, opts opOptions, res *config.Preset
} }
} }
func processLinks(prof config.PresetProfile, opts opOptions, res *config.PresetResult) { func processLinks(prof config.PresetProfile, opts opOptions, res *Result) {
for _, entry := range prof.Links { for _, entry := range prof.Links {
handle := func(app string) { handle := func(app string) {
...@@ -58,31 +58,40 @@ func processLinks(prof config.PresetProfile, opts opOptions, res *config.PresetR ...@@ -58,31 +58,40 @@ func processLinks(prof config.PresetProfile, opts opOptions, res *config.PresetR
} }
} }
func processReleaseReplace(prof config.PresetProfile, opts opOptions, res *config.PresetResult) { func processReleaseReplace(prof config.PresetProfile, opts opOptions, res *Result) {
sysVer := getSystemVersion() sysVer := getSystemVersion()
for _, rr := range prof.ReleaseReplace { for _, rr := range prof.ReleaseReplace {
src := expandPath(rr.Src, "") src := expandPath(rr.Src, "")
dest := expandPath(rr.Dest, "") dest := expandPath(rr.Dest, "")
msg := handleReleaseReplace(src, dest, sysVer, opts) item := handleReleaseReplace(src, dest, sysVer, opts)
res.Replaced = append(res.Replaced, msg) res.Replaced = append(res.Replaced, item)
} }
} }
func handleReleaseReplace(src, dest, sysVer string, opts opOptions) string { func handleReleaseReplace(src, dest, sysVer string, opts opOptions) ui.TreeItem {
if _, err := os.Stat(dest); os.IsNotExist(err) { if _, err := os.Stat(dest); os.IsNotExist(err) {
return fmt.Sprintf("Не найден: '%s' — пропущен", dest) return ui.TreeItem{
Name: fmt.Sprintf("Не найден: '%s'", dest),
Status: config.OpStatus.Skipped,
}
} }
data, err := os.ReadFile(dest) data, err := os.ReadFile(dest)
if err != nil { if err != nil {
return fmt.Sprintf("Не удалось прочитать %s: %v", dest, err) return ui.TreeItem{
Name: fmt.Sprintf("Не удалось прочитать %s: %v", dest, err),
Status: config.OpStatus.Error,
}
} }
content := string(data) content := string(data)
if strings.Contains(content, "XIMPER_LOCK") { if strings.Contains(content, "XIMPER_LOCK") {
return fmt.Sprintf("Заблокирован: %s — пропущен", dest) return ui.TreeItem{
Name: fmt.Sprintf("Заблокирован: %s", dest),
Status: config.OpStatus.Skipped,
}
} }
fileVer := getFileVersionTag(dest) fileVer := getFileVersionTag(dest)
...@@ -91,59 +100,83 @@ func handleReleaseReplace(src, dest, sysVer string, opts opOptions) string { ...@@ -91,59 +100,83 @@ func handleReleaseReplace(src, dest, sysVer string, opts opOptions) string {
} }
if fileVer == sysVer { if fileVer == sysVer {
return fmt.Sprintf("Актуально: '%s' — пропущено", dest) return ui.TreeItem{
Name: fmt.Sprintf("Актуально: '%s'", dest),
Status: config.OpStatus.Skipped,
}
} }
if opts.DryRun { if opts.DryRun {
return fmt.Sprintf("[Dry-run] Заменён: %s на %s", dest, src) return ui.TreeItem{
Name: fmt.Sprintf("%s → %s", src, dest),
Status: config.OpStatus.DryRun,
}
} }
_ = os.Rename(dest, dest+".old") _ = os.Rename(dest, dest+".old")
if err := copyFile(src, dest); err != nil { if err := copyFile(src, dest); err != nil {
return fmt.Sprintf("Ошибка замены %s: %v", dest, err) return ui.TreeItem{
Name: fmt.Sprintf("Ошибка замены %s: %v", dest, err),
Status: config.OpStatus.Error,
}
} }
return fmt.Sprintf("Обновлён: %s (%s → %s)", dest, fileVer, sysVer) return ui.TreeItem{
Name: fmt.Sprintf("%s (%s → %s)", dest, fileVer, sysVer),
Status: config.OpStatus.Done,
}
} }
func processHyprVars(manager *hyprland.HyprlandManager, prof config.PresetProfile, opts opOptions, res *config.PresetResult) { func processHyprVars(manager *hyprland.HyprlandManager, prof config.PresetProfile, opts opOptions, res *Result) {
for _, v := range prof.Hyprland.Vars { for _, v := range prof.Hyprland.Vars {
if !v.Force { if !v.Force {
if V := manager.GetVar(v.Var); V != "" { if V := manager.GetVar(v.Var); V != "" {
res.HyprVars = append(res.HyprVars, res.HyprVars = append(res.HyprVars, ui.TreeItem{
fmt.Sprintf("Уже существует: '%s' — пропущено", v.Var)) Name: fmt.Sprintf("Уже существует: '%s'", v.Var),
Status: config.OpStatus.Skipped,
})
continue continue
} }
} }
if opts.DryRun { if opts.DryRun {
res.HyprVars = append(res.HyprVars, res.HyprVars = append(res.HyprVars, ui.TreeItem{
fmt.Sprintf("[Dry-run] Установлена переменная: %s = %s", v.Var, v.Value)) Name: fmt.Sprintf("%s = %s", v.Var, v.Value),
Status: config.OpStatus.DryRun,
})
continue continue
} }
if _, err := manager.SetVar(v.Var, v.Value); err != nil { if _, err := manager.SetVar(v.Var, v.Value); err != nil {
res.HyprVars = append(res.HyprVars, res.HyprVars = append(res.HyprVars, ui.TreeItem{
fmt.Sprintf("Ошибка установки %s: %v", v.Var, err)) Name: fmt.Sprintf("Ошибка установки %s: %v", v.Var, err),
Status: config.OpStatus.Error,
})
} else { } else {
res.HyprVars = append(res.HyprVars, res.HyprVars = append(res.HyprVars, ui.TreeItem{
fmt.Sprintf("Установлена переменная: %s = %s", v.Var, v.Value)) Name: fmt.Sprintf("%s = %s", v.Var, v.Value),
Status: config.OpStatus.Done,
})
} }
} }
} }
func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetProfile, opts opOptions, res *config.PresetResult) { func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetProfile, opts opOptions, res *Result) {
for _, m := range prof.Hyprland.Modules { for _, m := range prof.Hyprland.Modules {
if m.Module == "" { if m.Module == "" {
res.HyprModules = append(res.HyprModules, "Пропущен модуль без имени") res.HyprModules = append(res.HyprModules, ui.TreeItem{
Name: "Пропущен модуль без имени",
Status: config.OpStatus.Skipped,
})
continue continue
} }
if !slices.Contains(validActions, m.Action) { if !slices.Contains(validActions, m.Action) {
res.HyprModules = append(res.HyprModules, res.HyprModules = append(res.HyprModules, ui.TreeItem{
fmt.Sprintf("Модуль '%s' пропущен: неподдерживаемый action", Name: fmt.Sprintf("Модуль '%s': неподдерживаемый action", m.Module),
m.Module)) Status: config.OpStatus.Skipped,
})
continue continue
} }
...@@ -152,24 +185,28 @@ func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetPro ...@@ -152,24 +185,28 @@ func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetPro
cmd := exec.Command("sh", "-c", m.IfExec) cmd := exec.Command("sh", "-c", m.IfExec)
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
res.HyprModules = append(res.HyprModules, res.HyprModules = append(res.HyprModules, ui.TreeItem{
fmt.Sprintf("Модуль '%s' пропущен: команда '%s' завершилась с ошибкой", Name: fmt.Sprintf("Модуль '%s': команда '%s' завершилась с ошибкой", m.Module, m.IfExec),
m.Module, m.IfExec)) Status: config.OpStatus.Skipped,
})
continue continue
} }
if len(bytes.TrimSpace(output)) == 0 { if len(bytes.TrimSpace(output)) == 0 {
res.HyprModules = append(res.HyprModules, res.HyprModules = append(res.HyprModules, ui.TreeItem{
fmt.Sprintf("Модуль '%s' пропущен: команда '%s' не вернула данных", Name: fmt.Sprintf("Модуль '%s': команда '%s' не вернула данных", m.Module, m.IfExec),
m.Module, m.IfExec)) Status: config.OpStatus.Skipped,
})
continue continue
} }
} }
// if-binary // if-binary
if m.IfBinary != "" && !commandExists(m.IfBinary) { if m.IfBinary != "" && !commandExists(m.IfBinary) {
res.HyprModules = append(res.HyprModules, res.HyprModules = append(res.HyprModules, ui.TreeItem{
fmt.Sprintf("Модуль '%s' пропущен: бинарник '%s' не найден", m.Module, m.IfBinary)) Name: fmt.Sprintf("Модуль '%s': бинарник '%s' не найден", m.Module, m.IfBinary),
Status: config.OpStatus.Skipped,
})
continue continue
} }
...@@ -178,28 +215,35 @@ func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetPro ...@@ -178,28 +215,35 @@ func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetPro
if _, err := os.Stat(modulefile); os.IsNotExist(err) { if _, err := os.Stat(modulefile); os.IsNotExist(err) {
if opts.DryRun { if opts.DryRun {
res.HyprModules = append(res.HyprModules, res.HyprModules = append(res.HyprModules, ui.TreeItem{
fmt.Sprintf("[Dry-run] Создан пустой модуль: %s", m.Module)) Name: fmt.Sprintf("Создан пустой модуль: %s", m.Module),
Status: config.OpStatus.DryRun,
})
} else { } else {
_ = os.WriteFile(modulefile, []byte(""), 0o644) _ = os.WriteFile(modulefile, []byte(""), 0o644)
res.HyprModules = append(res.HyprModules, res.HyprModules = append(res.HyprModules, ui.TreeItem{
fmt.Sprintf("Создан пустой модуль: %s", m.Module)) Name: fmt.Sprintf("Создан пустой модуль: %s", m.Module),
Status: config.OpStatus.Done,
})
} }
} }
} }
// dry-run // dry-run
if opts.DryRun { if opts.DryRun {
var dryrunmessage string var msg string
switch m.Action { switch m.Action {
case "enable": case "enable":
dryrunmessage = fmt.Sprintf("[Dry-run] Подключён модуль: %s", m.Module) msg = fmt.Sprintf("Подключён модуль: %s", m.Module)
case "disable": case "disable":
dryrunmessage = fmt.Sprintf("[Dry-run] Отключён модуль: %s", m.Module) msg = fmt.Sprintf("Отключён модуль: %s", m.Module)
case "remove": case "remove":
dryrunmessage = fmt.Sprintf("[Dry-run] Удалён модуль: %s", m.Module) msg = fmt.Sprintf("Удалён модуль: %s", m.Module)
} }
res.HyprModules = append(res.HyprModules, dryrunmessage) res.HyprModules = append(res.HyprModules, ui.TreeItem{
Name: msg,
Status: config.OpStatus.DryRun,
})
continue continue
} }
...@@ -209,38 +253,47 @@ func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetPro ...@@ -209,38 +253,47 @@ func processHyprModules(manager *hyprland.HyprlandManager, prof config.PresetPro
m.User, m.User,
m.NewOnly, m.NewOnly,
); err != nil { ); err != nil {
res.HyprModules = append(res.HyprModules, res.HyprModules = append(res.HyprModules, ui.TreeItem{
fmt.Sprintf("Ошибка (%s): %v", m.Module, err)) Name: fmt.Sprintf("Ошибка (%s): %v", m.Module, err),
Status: config.OpStatus.Error,
})
} else { } else {
var message string var msg string
switch m.Action { switch m.Action {
case "enable": case "enable":
message = fmt.Sprintf("Подключён модуль: %s", m.Module) msg = fmt.Sprintf("Подключён модуль: %s", m.Module)
case "disable": case "disable":
message = fmt.Sprintf("Отключён модуль: %s", m.Module) msg = fmt.Sprintf("Отключён модуль: %s", m.Module)
case "remove": case "remove":
message = fmt.Sprintf("Удалён модуль: %s", m.Module) msg = fmt.Sprintf("Удалён модуль: %s", m.Module)
} }
res.HyprModules = append(res.HyprModules, message) res.HyprModules = append(res.HyprModules, ui.TreeItem{
Name: msg,
Status: config.OpStatus.Done,
})
} }
} }
} }
func processHyprLayoutSync(manager *hyprland.HyprlandManager, prof config.PresetProfile, opts opOptions, res *config.PresetResult) { func processHyprLayoutSync(manager *hyprland.HyprlandManager, prof config.PresetProfile, opts opOptions, res *Result) {
if !prof.Hyprland.Option.SyncSystemLayouts { if !prof.Hyprland.Option.SyncSystemLayouts {
return return
} }
if opts.DryRun { if opts.DryRun {
res.SyncSystemLayouts = append(res.SyncSystemLayouts, res.SyncSystemLayouts = append(res.SyncSystemLayouts, ui.TreeItem{
"[Dry-run] Синхронизация раскладок Hyprland пропущена") Name: "Синхронизация раскладок Hyprland",
Status: config.OpStatus.DryRun,
})
return return
} }
sysLayouts, err := hyprland.HyprlandGetKeyboardLayouts() sysLayouts, err := hyprland.HyprlandGetKeyboardLayouts()
if err != nil { if err != nil {
res.SyncSystemLayouts = append(res.SyncSystemLayouts, res.SyncSystemLayouts = append(res.SyncSystemLayouts, ui.TreeItem{
"Не удалось получить системные раскладки") Name: "Не удалось получить системные раскладки",
Status: config.OpStatus.Error,
})
return return
} }
...@@ -250,48 +303,73 @@ func processHyprLayoutSync(manager *hyprland.HyprlandManager, prof config.Preset ...@@ -250,48 +303,73 @@ func processHyprLayoutSync(manager *hyprland.HyprlandManager, prof config.Preset
hyprLayouts = "<empty>" hyprLayouts = "<empty>"
} }
res.SyncSystemLayouts = append(res.SyncSystemLayouts, res.SyncSystemLayouts = append(res.SyncSystemLayouts, ui.TreeItem{
fmt.Sprintf("Раскладка XCB: %s", sysLayouts)) Name: fmt.Sprintf("Раскладка XCB: %s", sysLayouts),
res.SyncSystemLayouts = append(res.SyncSystemLayouts, Status: config.OpStatus.Done,
fmt.Sprintf("Раскладка Hyprland: %s", hyprLayouts)) })
res.SyncSystemLayouts = append(res.SyncSystemLayouts, ui.TreeItem{
Name: fmt.Sprintf("Раскладка Hyprland: %s", hyprLayouts),
Status: config.OpStatus.Done,
})
if hyprLayouts == "<empty>" { if hyprLayouts == "<empty>" {
if _, err := manager.SetVar("kb_layout", sysLayouts); err != nil { if _, err := manager.SetVar("kb_layout", sysLayouts); err != nil {
res.SyncSystemLayouts = append(res.SyncSystemLayouts, res.SyncSystemLayouts = append(res.SyncSystemLayouts, ui.TreeItem{
"Не удалось обновить kb_layout") Name: "Не удалось обновить kb_layout",
Status: config.OpStatus.Error,
})
return return
} }
res.SyncSystemLayouts = append(res.SyncSystemLayouts, res.SyncSystemLayouts = append(res.SyncSystemLayouts, ui.TreeItem{
fmt.Sprintf("Раскладка обновлена: %s", sysLayouts)) Name: fmt.Sprintf("Раскладка обновлена: %s", sysLayouts),
Status: config.OpStatus.Done,
})
} else { } else {
res.SyncSystemLayouts = append(res.SyncSystemLayouts, res.SyncSystemLayouts = append(res.SyncSystemLayouts, ui.TreeItem{
fmt.Sprintf("Раскладка уже установлена: %s", hyprLayouts)) Name: fmt.Sprintf("Раскладка уже установлена: %s", hyprLayouts),
Status: config.OpStatus.Skipped,
})
} }
} }
func processSystemGrub(prof config.PresetProfile, opts opOptions, res *config.PresetResult) { func processSystemGrub(prof config.PresetProfile, opts opOptions, res *Result) {
if prof.System.Grub.FixResolution { if prof.System.Grub.FixResolution {
autoUpdate := prof.System.Grub.Update autoUpdate := prof.System.Grub.Update
if opts.DryRun { if opts.DryRun {
res.System = append(res.System, "[Dry-run] GRUB_GFXMODE не исправлен") res.System = append(res.System, ui.TreeItem{
Name: "GRUB_GFXMODE",
Status: config.OpStatus.DryRun,
})
if autoUpdate { if autoUpdate {
res.System = append(res.System, "[Dry-run] update-grub не выполнен") res.System = append(res.System, ui.TreeItem{
Name: "update-grub",
Status: config.OpStatus.DryRun,
})
} }
} else { } else {
err := system.SystemGrubFixResolution(autoUpdate, false) err := system.SystemGrubFixResolution(autoUpdate, false)
if err != nil { if err != nil {
res.System = append(res.System, fmt.Sprintf("Ошибка GRUB: %v", err)) res.System = append(res.System, ui.TreeItem{
Name: fmt.Sprintf("Ошибка GRUB: %v", err),
Status: config.OpStatus.Error,
})
} else { } else {
res.System = append(res.System, "GRUB_GFXMODE исправлен") res.System = append(res.System, ui.TreeItem{
Name: "GRUB_GFXMODE исправлен",
Status: config.OpStatus.Done,
})
if autoUpdate { if autoUpdate {
res.System = append(res.System, "update-grub выполнен") res.System = append(res.System, ui.TreeItem{
Name: "update-grub выполнен",
Status: config.OpStatus.Done,
})
} }
} }
} }
} }
} }
func processProfile(prof config.PresetProfile, dryRun bool, res *config.PresetResult) { func processProfile(prof config.PresetProfile, dryRun bool, res *Result) {
opts := opOptions{DryRun: dryRun} opts := opOptions{DryRun: dryRun}
processCopies(prof, opts, res) processCopies(prof, opts, res)
...@@ -309,7 +387,7 @@ func processProfile(prof config.PresetProfile, dryRun bool, res *config.PresetRe ...@@ -309,7 +387,7 @@ func processProfile(prof config.PresetProfile, dryRun bool, res *config.PresetRe
processHyprLayoutSync(manager, prof, opts, res) processHyprLayoutSync(manager, prof, opts, res)
} }
func createProfile(profileName string, prof config.PresetProfile, dryRun bool, res *config.PresetResult) { func createProfile(profileName string, prof config.PresetProfile, dryRun bool, res *Result) {
color.Green("Создаётся профиль: %s", profileName) color.Green("Создаётся профиль: %s", profileName)
if !profileAvailable(prof) { if !profileAvailable(prof) {
color.Red("Профиль %s недоступен", profileName) color.Red("Профиль %s недоступен", profileName)
...@@ -364,7 +442,7 @@ func presetApplyCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -364,7 +442,7 @@ func presetApplyCommand(ctx context.Context, cmd *cli.Command) error {
return nil return nil
} }
res := &config.PresetResult{} res := &Result{}
createProfile(profileName, mainProf, dryRun, res) createProfile(profileName, mainProf, dryRun, res)
renderPresetResult(res) renderPresetResult(res)
...@@ -388,7 +466,7 @@ func presetApplyAllCommand(ctx context.Context, cmd *cli.Command) error { ...@@ -388,7 +466,7 @@ func presetApplyAllCommand(ctx context.Context, cmd *cli.Command) error {
continue continue
} }
res := &config.PresetResult{} res := &Result{}
createProfile(name, prof, dryRun, res) createProfile(name, prof, dryRun, res)
renderPresetResult(res) renderPresetResult(res)
} }
......
...@@ -34,25 +34,27 @@ func profileStatus(p config.PresetProfile) config.ItemStatus { ...@@ -34,25 +34,27 @@ func profileStatus(p config.PresetProfile) config.ItemStatus {
return config.ProfileStatus.Unavailable return config.ProfileStatus.Unavailable
} }
func AppendOpResult(res *config.PresetResult, r opResult) { func AppendOpResult(res *Result, r opResult) {
var msg string item := ui.TreeItem{
Status: r.Status,
}
switch r.Status { switch {
case StatusSkipped: case r.Status.IsEqual(config.OpStatus.Skipped):
msg = fmt.Sprintf("Уже существует: %s — пропущено", r.Target) item.Name = fmt.Sprintf("Уже существует: %s", r.Target)
case StatusDryRun: case r.Status.IsEqual(config.OpStatus.DryRun):
msg = fmt.Sprintf("[Dry-run] %s → %s", r.Source, r.Target) item.Name = fmt.Sprintf("%s → %s", r.Source, r.Target)
case StatusDone: case r.Status.IsEqual(config.OpStatus.Done):
msg = fmt.Sprintf("Выполнено: %s → %s", r.Source, r.Target) item.Name = fmt.Sprintf("%s → %s", r.Source, r.Target)
case StatusError: case r.Status.IsEqual(config.OpStatus.Error):
msg = fmt.Sprintf("Ошибка: %s", r.Reason) item.Name = fmt.Sprintf("Ошибка: %s", r.Reason)
} }
switch r.Kind { switch r.Kind {
case OpCopy: case OpCopy:
res.Copies = append(res.Copies, msg) res.Copies = append(res.Copies, item)
case OpLink: case OpLink:
res.Links = append(res.Links, msg) res.Links = append(res.Links, item)
} }
} }
...@@ -78,12 +80,12 @@ func expandPath(path, name string) string { ...@@ -78,12 +80,12 @@ func expandPath(path, name string) string {
return path return path
} }
func renderPresetResult(res *config.PresetResult) { func renderPresetResult(res *Result) {
ui.RenderTreeLines("Копируем", res.Copies) ui.RenderTreeItems("Копируем", res.Copies)
ui.RenderTreeLines("Создаём ссылки", res.Links) ui.RenderTreeItems("Создаём ссылки", res.Links)
ui.RenderTreeLines("Обновляем", res.Replaced) ui.RenderTreeItems("Обновляем", res.Replaced)
ui.RenderTreeLines("Настраиваем систему", res.System) ui.RenderTreeItems("Настраиваем систему", res.System)
ui.RenderTreeLines("Создаём переменные Hyprland", res.HyprVars) ui.RenderTreeItems("Создаём переменные Hyprland", res.HyprVars)
ui.RenderTreeLines("Подключаем модули Hyprland", res.HyprModules) ui.RenderTreeItems("Подключаем модули Hyprland", res.HyprModules)
ui.RenderTreeLines("Синхронизируем раскладку Hyprland", res.SyncSystemLayouts) ui.RenderTreeItems("Синхронизируем раскладку Hyprland", res.SyncSystemLayouts)
} }
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