T3.5: Move display helpers to src/app/format.go
Extract pure display/formatting functions from the GUI: - StatusText, EventText, DisplayFolder, DisplayArguments, DisplaySuccessExitCodes, DisplayRunMode, DisplayInvocation, DisplayIndex. These have no Fyne dependencies and serve as a shared formatting layer for the service to use (e.g. StatusText in the tests when mocking the GUI). The GUI imports them as app.DisplayFolder(...) etc. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.mixdep.ru/mix/gosentry/src/domain"
|
||||
)
|
||||
|
||||
// StatusText formats a job's current state for display: "Paused" if disabled,
|
||||
// else its runtime LastState (Ready, Running, Success, etc).
|
||||
func StatusText(j domain.Job, runtime *domain.JobRuntime) string {
|
||||
if !j.Enabled {
|
||||
return "Paused"
|
||||
}
|
||||
if runtime == nil {
|
||||
return ""
|
||||
}
|
||||
return runtime.LastState
|
||||
}
|
||||
|
||||
// EventText formats a run record for the History table, showing time, trigger,
|
||||
// job name, outcome state, detail, and log file (if any).
|
||||
func EventText(e domain.RunRecord) string {
|
||||
trigger := e.Trigger
|
||||
if trigger == "" {
|
||||
trigger = "Unknown"
|
||||
}
|
||||
if e.LogFile != "" {
|
||||
return fmt.Sprintf("%s %s %s %s %s %s", e.Time, trigger, e.JobName, e.State, e.Detail, e.LogFile)
|
||||
}
|
||||
return fmt.Sprintf("%s %s %s %s %s", e.Time, trigger, e.JobName, e.State, e.Detail)
|
||||
}
|
||||
|
||||
// DisplayFolder formats a job's folder for display: "(No folder)" if empty,
|
||||
// else the trimmed folder name.
|
||||
func DisplayFolder(folder string) string {
|
||||
if strings.TrimSpace(folder) == "" {
|
||||
return "(No folder)"
|
||||
}
|
||||
return strings.TrimSpace(folder)
|
||||
}
|
||||
|
||||
// DisplayArguments formats a job's arguments for display: "(none)" if empty,
|
||||
// else the trimmed arguments.
|
||||
func DisplayArguments(arguments string) string {
|
||||
if strings.TrimSpace(arguments) == "" {
|
||||
return "(none)"
|
||||
}
|
||||
return strings.TrimSpace(arguments)
|
||||
}
|
||||
|
||||
// DisplaySuccessExitCodes formats a job's success exit codes for display:
|
||||
// "0" (the default) if empty, else the trimmed codes.
|
||||
func DisplaySuccessExitCodes(codes string) string {
|
||||
if strings.TrimSpace(codes) == "" {
|
||||
return "0"
|
||||
}
|
||||
return strings.TrimSpace(codes)
|
||||
}
|
||||
|
||||
// DisplayRunMode formats a job's execution mode: "Start only" or
|
||||
// "Wait for completion".
|
||||
func DisplayRunMode(job domain.Job) string {
|
||||
if job.StartOnly {
|
||||
return "Start only"
|
||||
}
|
||||
return "Wait for completion"
|
||||
}
|
||||
|
||||
// DisplayInvocation formats a job's command and arguments for the jobs list,
|
||||
// joining them with spacing and collapsing newlines in arguments to spaces.
|
||||
func DisplayInvocation(job domain.Job) string {
|
||||
if strings.TrimSpace(job.Arguments) == "" {
|
||||
return job.Command
|
||||
}
|
||||
return job.Command + " " + strings.ReplaceAll(strings.TrimSpace(job.Arguments), "\n", " ")
|
||||
}
|
||||
|
||||
// DisplayIndex returns the position of jobIndex in the given slice of indexes,
|
||||
// or 0 if not found.
|
||||
func DisplayIndex(indexes []int, jobIndex int) int {
|
||||
for display, index := range indexes {
|
||||
if index == jobIndex {
|
||||
return display
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
+12
-76
@@ -211,8 +211,8 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) {
|
||||
schedule := newJobDetailLabel(jobs[selected].Schedule)
|
||||
command := newJobDetailLabel(jobs[selected].Command)
|
||||
arguments := newJobDetailLabel(jobs[selected].Arguments)
|
||||
successExitCodes := newJobDetailLabel(displaySuccessExitCodes(jobs[selected].SuccessExitCodes))
|
||||
runMode := newJobDetailLabel(displayRunMode(jobs[selected]))
|
||||
successExitCodes := newJobDetailLabel(app.DisplaySuccessExitCodes(jobs[selected].SuccessExitCodes))
|
||||
runMode := newJobDetailLabel(app.DisplayRunMode(jobs[selected]))
|
||||
selectedRuntime := runtimeFor(selected)
|
||||
lastRun := newJobDetailLabel(selectedRuntime.LastRun)
|
||||
nextRun := newJobDetailLabel(selectedRuntime.NextRun)
|
||||
@@ -245,7 +245,7 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) {
|
||||
},
|
||||
func() fyne.CanvasObject { return widget.NewLabel("log") },
|
||||
func(id widget.ListItemID, item fyne.CanvasObject) {
|
||||
item.(*widget.Label).SetText(eventText(selectedLogs[id]))
|
||||
item.(*widget.Label).SetText(app.EventText(selectedLogs[id]))
|
||||
},
|
||||
)
|
||||
|
||||
@@ -271,12 +271,12 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) {
|
||||
current := jobs[selected]
|
||||
runtime := runtimeFor(selected)
|
||||
title.SetText(current.Name)
|
||||
folder.SetText(displayFolder(current.Folder))
|
||||
folder.SetText(app.DisplayFolder(current.Folder))
|
||||
schedule.SetText(current.Schedule)
|
||||
command.SetText(current.Command)
|
||||
arguments.SetText(displayArguments(current.Arguments))
|
||||
successExitCodes.SetText(displaySuccessExitCodes(current.SuccessExitCodes))
|
||||
runMode.SetText(displayRunMode(current))
|
||||
arguments.SetText(app.DisplayArguments(current.Arguments))
|
||||
successExitCodes.SetText(app.DisplaySuccessExitCodes(current.SuccessExitCodes))
|
||||
runMode.SetText(app.DisplayRunMode(current))
|
||||
lastRun.SetText(runtime.LastRun)
|
||||
nextRun.SetText(runtime.NextRun)
|
||||
state.SetText(runtime.LastState)
|
||||
@@ -312,8 +312,8 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) {
|
||||
name.SetText(current.Name)
|
||||
// Keep each row compact: folder, schedule, and command are shown in one
|
||||
// metadata line so the left pane stays useful even with many jobs.
|
||||
meta.SetText(displayFolder(current.Folder) + " " + current.Schedule + " " + displayInvocation(current))
|
||||
status.SetText(statusText(current, runtimes[current.ID]))
|
||||
meta.SetText(app.DisplayFolder(current.Folder) + " " + current.Schedule + " " + app.DisplayInvocation(current))
|
||||
status.SetText(app.StatusText(current, runtimes[current.ID]))
|
||||
},
|
||||
)
|
||||
list.OnSelected = func(id widget.ListItemID) {
|
||||
@@ -366,7 +366,7 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) {
|
||||
selected = indexOfID(jobs, created.ID)
|
||||
filteredJobs = filteredJobIndexes(jobs, selectedFolder)
|
||||
list.Refresh()
|
||||
list.Select(displayIndex(filteredJobs, selected))
|
||||
list.Select(app.DisplayIndex(filteredJobs, selected))
|
||||
refresh()
|
||||
})
|
||||
})
|
||||
@@ -480,7 +480,7 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) {
|
||||
}
|
||||
list.Refresh()
|
||||
if selected >= 0 {
|
||||
list.Select(displayIndex(filteredJobs, selected))
|
||||
list.Select(app.DisplayIndex(filteredJobs, selected))
|
||||
}
|
||||
refresh()
|
||||
}, w)
|
||||
@@ -569,16 +569,6 @@ func (layout minWidthLayout) Layout(objects []fyne.CanvasObject, size fyne.Size)
|
||||
}
|
||||
}
|
||||
|
||||
func statusText(j job, runtime *domain.JobRuntime) string {
|
||||
if !j.Enabled {
|
||||
return "Paused"
|
||||
}
|
||||
if runtime == nil {
|
||||
return ""
|
||||
}
|
||||
return runtime.LastState
|
||||
}
|
||||
|
||||
func newEvent(jobID int, jobName string, state string, detail string) event {
|
||||
// Use the same timestamp shape as command run records so the History tab is
|
||||
// visually consistent across startup, UI actions, manual runs, and schedules.
|
||||
@@ -592,17 +582,6 @@ func newEvent(jobID int, jobName string, state string, detail string) event {
|
||||
}
|
||||
}
|
||||
|
||||
func eventText(e event) string {
|
||||
trigger := e.Trigger
|
||||
if trigger == "" {
|
||||
trigger = "Unknown"
|
||||
}
|
||||
if e.LogFile != "" {
|
||||
return fmt.Sprintf("%s %s %s %s %s %s", e.Time, trigger, e.JobName, e.State, e.Detail, e.LogFile)
|
||||
}
|
||||
return fmt.Sprintf("%s %s %s %s %s", e.Time, trigger, e.JobName, e.State, e.Detail)
|
||||
}
|
||||
|
||||
func collectActivity(jobs []job, runtimes map[int]*domain.JobRuntime) []event {
|
||||
var events []event
|
||||
for _, current := range jobs {
|
||||
@@ -689,49 +668,6 @@ func filterValue(folder string) string {
|
||||
return strings.TrimSpace(folder)
|
||||
}
|
||||
|
||||
func displayFolder(folder string) string {
|
||||
if strings.TrimSpace(folder) == "" {
|
||||
return "(" + noFolder + ")"
|
||||
}
|
||||
return strings.TrimSpace(folder)
|
||||
}
|
||||
|
||||
func displayArguments(arguments string) string {
|
||||
if strings.TrimSpace(arguments) == "" {
|
||||
return "(none)"
|
||||
}
|
||||
return strings.TrimSpace(arguments)
|
||||
}
|
||||
|
||||
func displaySuccessExitCodes(codes string) string {
|
||||
if strings.TrimSpace(codes) == "" {
|
||||
return "0"
|
||||
}
|
||||
return strings.TrimSpace(codes)
|
||||
}
|
||||
|
||||
func displayRunMode(current job) string {
|
||||
if current.StartOnly {
|
||||
return "Start only"
|
||||
}
|
||||
return "Wait for completion"
|
||||
}
|
||||
|
||||
func displayInvocation(current job) string {
|
||||
if strings.TrimSpace(current.Arguments) == "" {
|
||||
return current.Command
|
||||
}
|
||||
return current.Command + " " + strings.ReplaceAll(strings.TrimSpace(current.Arguments), "\n", " ")
|
||||
}
|
||||
|
||||
func displayIndex(indexes []int, jobIndex int) int {
|
||||
for display, index := range indexes {
|
||||
if index == jobIndex {
|
||||
return display
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) {
|
||||
name := widget.NewEntry()
|
||||
@@ -751,7 +687,7 @@ func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) {
|
||||
arguments.SetText(current.Arguments)
|
||||
successExitCodes := widget.NewEntry()
|
||||
successExitCodes.SetPlaceHolder("0")
|
||||
successExitCodes.SetText(displaySuccessExitCodes(current.SuccessExitCodes))
|
||||
successExitCodes.SetText(app.DisplaySuccessExitCodes(current.SuccessExitCodes))
|
||||
startOnly := widget.NewCheck("Start only, do not wait for exit", nil)
|
||||
startOnly.SetChecked(current.StartOnly)
|
||||
enabled := widget.NewCheck("Enabled", nil)
|
||||
|
||||
Reference in New Issue
Block a user