Refactoring complete: v0.4.0 architectural milestone (#1)
## Summary Completed Phase 5 refactoring and reached the target architecture. **Architectural milestone achieved:** - Service layer owns all state and is the sole writer - UI is a thin Fyne view, all widget updates marshaled via `fyne.Do` - Core engines are stateless and injectable - Domain types are pure (no `yaml:"-"` fields) - Full module builds and `go vet ./...` clean ## Changes - Bump version: 0.3.6 → 0.4.0 - Update CHANGELOG with Phase 5 summary - Add ROADMAP "Refactoring Follow-Ups" section ## Known follow-up work 1. **Linux test build broken** — `runner_test.go` needs `//go:build windows` tag 2. **File-size limits exceeded** — `operations.go` (486 lines), `jobs_view.go` (415 lines) See ROADMAP.md for details. --------- Co-authored-by: mixeme <mix.public@ya.ru> Reviewed-on: #1
This commit was merged in pull request #1.
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
|
||||
}
|
||||
Reference in New Issue
Block a user