1package modes
2
3import (
4 "context"
5 "fmt"
6 "strings"
7 "time"
8
9 "github.com/patriceckhart/zot/packages/agent/extensions"
10 "github.com/patriceckhart/zot/packages/tui"
11)
12
13const reloadStatusDuration = 5 * time.Second
14
15func formatReloadStatus(stats extensions.ReloadStats) (string, bool) {
16 msg := fmt.Sprintf("reloaded: %d stopped, %d loaded (%d ready)", stats.Stopped, stats.Loaded, stats.Ready)
17 if len(stats.Errors) == 0 {
18 return msg, false
19 }
20 details := make([]string, 0, len(stats.Errors))
21 for _, err := range stats.Errors {
22 if err != nil {
23 details = append(details, err.Error())
24 }
25 }
26 msg += fmt.Sprintf(", %d error(s)", len(stats.Errors))
27 if len(details) > 0 {
28 msg += ": " + strings.Join(details, "; ")
29 }
30 return msg, true
31}
32
33func (i *Interactive) setReloadStatus(msg string, failed bool) uint64 {
34 i.mu.Lock()
35 defer i.mu.Unlock()
36 i.reloadStatusSeq++
37 if failed {
38 i.statusOK = ""
39 i.statusErr = msg
40 i.reloadErrors = append(i.reloadErrors, msg)
41 return i.reloadStatusSeq
42 }
43 i.statusOK = msg
44 i.statusErr = ""
45 i.reloadErrors = nil
46 return i.reloadStatusSeq
47}
48
49func renderReloadErrors(theme tui.Theme, errors []string, width int) []string {
50 const marker = "✖ "
51 const indent = " "
52 wrapWidth := width - len([]rune(marker))
53 if wrapWidth < 8 {
54 wrapWidth = 8
55 }
56 var out []string
57 for _, msg := range errors {
58 // Split explicit newlines before wrapping. Passing a multiline string
59 // to WrapANSILine would leave the terminal to interpret the embedded
60 // newline, which can misalign the continuation text.
61 for lineIndex, paragraph := range strings.Split(msg, "\n") {
62 wrapped := tui.WrapANSILine(paragraph, wrapWidth)
63 if len(wrapped) == 0 {
64 wrapped = []string{""}
65 }
66 for wrapIndex, line := range wrapped {
67 prefix := indent
68 if lineIndex == 0 && wrapIndex == 0 {
69 prefix = marker
70 }
71 out = append(out, theme.FG256(theme.Error, prefix+line))
72 }
73 }
74 }
75 return out
76}
77
78func (i *Interactive) clearReloadStatus(seq uint64, msg string, failed bool) bool {
79 i.mu.Lock()
80 defer i.mu.Unlock()
81 if i.reloadStatusSeq != seq {
82 return false
83 }
84 if failed {
85 if i.statusErr != msg {
86 return false
87 }
88 i.statusErr = ""
89 return true
90 }
91 if i.statusOK != msg {
92 return false
93 }
94 i.statusOK = ""
95 return true
96}
97
98func (i *Interactive) dismissReloadStatus(ctx context.Context, seq uint64, msg string, failed bool) {
99 timer := time.NewTimer(reloadStatusDuration)
100 defer timer.Stop()
101 select {
102 case <-ctx.Done():
103 return
104 case <-timer.C:
105 if i.clearReloadStatus(seq, msg, failed) {
106 i.invalidate()
107 }
108 }
109}