...
1 package progress
2
3 import (
4 "fmt"
5 "io"
6 "os"
7 "sync"
8 "time"
9 "unicode/utf8"
10
11 "github.com/jedib0t/go-pretty/v6/text"
12 )
13
14 var (
15
16 DefaultLengthTracker = 20
17
18
19
20 DefaultUpdateFrequency = time.Millisecond * 250
21 )
22
23
24 type Progress struct {
25 autoStop bool
26 done chan bool
27 lengthTracker int
28 lengthProgress int
29 lengthProgressOverall int
30 outputWriter io.Writer
31 logsToRender []string
32 logsToRenderMutex sync.RWMutex
33 messageWidth int
34 numTrackersExpected int64
35 overallTracker *Tracker
36 overallTrackerMutex sync.RWMutex
37 renderInProgress bool
38 renderInProgressMutex sync.RWMutex
39 sortBy SortBy
40 style *Style
41 trackerPosition Position
42 trackersActive []*Tracker
43 trackersActiveMutex sync.RWMutex
44 trackersDone []*Tracker
45 trackersDoneMutex sync.RWMutex
46 trackersInQueue []*Tracker
47 trackersInQueueMutex sync.RWMutex
48 updateFrequency time.Duration
49 }
50
51
52
53 type Position int
54
55 const (
56
57 PositionLeft Position = iota
58
59
60 PositionRight
61 )
62
63
64
65
66 func (p *Progress) AppendTracker(t *Tracker) {
67 t.start()
68
69 p.overallTrackerMutex.Lock()
70 if p.overallTracker == nil {
71 p.overallTracker = &Tracker{Total: 1}
72 if p.numTrackersExpected > 0 {
73 p.overallTracker.Total = p.numTrackersExpected * 100
74 }
75 p.overallTracker.start()
76 }
77 p.trackersInQueueMutex.Lock()
78 p.trackersInQueue = append(p.trackersInQueue, t)
79 p.trackersInQueueMutex.Unlock()
80 p.overallTracker.mutex.Lock()
81 if p.overallTracker.Total < int64(p.Length())*100 {
82 p.overallTracker.Total = int64(p.Length()) * 100
83 }
84 p.overallTracker.mutex.Unlock()
85 p.overallTrackerMutex.Unlock()
86 }
87
88
89 func (p *Progress) AppendTrackers(trackers []*Tracker) {
90 for _, tracker := range trackers {
91 p.AppendTracker(tracker)
92 }
93 }
94
95
96
97 func (p *Progress) IsRenderInProgress() bool {
98 p.renderInProgressMutex.RLock()
99 defer p.renderInProgressMutex.RUnlock()
100
101 return p.renderInProgress
102 }
103
104
105 func (p *Progress) Length() int {
106 p.trackersActiveMutex.RLock()
107 p.trackersDoneMutex.RLock()
108 p.trackersInQueueMutex.RLock()
109 out := len(p.trackersInQueue) + len(p.trackersActive) + len(p.trackersDone)
110 p.trackersInQueueMutex.RUnlock()
111 p.trackersDoneMutex.RUnlock()
112 p.trackersActiveMutex.RUnlock()
113
114 return out
115 }
116
117
118 func (p *Progress) LengthActive() int {
119 p.trackersActiveMutex.RLock()
120 p.trackersInQueueMutex.RLock()
121 out := len(p.trackersInQueue) + len(p.trackersActive)
122 p.trackersInQueueMutex.RUnlock()
123 p.trackersActiveMutex.RUnlock()
124
125 return out
126 }
127
128
129 func (p *Progress) LengthDone() int {
130 p.trackersDoneMutex.RLock()
131 out := len(p.trackersDone)
132 p.trackersDoneMutex.RUnlock()
133
134 return out
135 }
136
137
138
139 func (p *Progress) LengthInQueue() int {
140 p.trackersInQueueMutex.RLock()
141 out := len(p.trackersInQueue)
142 p.trackersInQueueMutex.RUnlock()
143
144 return out
145 }
146
147
148
149 func (p *Progress) Log(msg string, a ...interface{}) {
150 if len(a) > 0 {
151 msg = fmt.Sprintf(msg, a...)
152 }
153 p.logsToRenderMutex.Lock()
154 p.logsToRender = append(p.logsToRender, msg)
155 p.logsToRenderMutex.Unlock()
156 }
157
158
159
160
161
162 func (p *Progress) SetAutoStop(autoStop bool) {
163 p.autoStop = autoStop
164 }
165
166
167
168
169 func (p *Progress) SetMessageWidth(width int) {
170 p.messageWidth = width
171 }
172
173
174
175 func (p *Progress) SetNumTrackersExpected(numTrackers int) {
176 p.numTrackersExpected = int64(numTrackers)
177 }
178
179
180
181
182 func (p *Progress) SetOutputWriter(writer io.Writer) {
183 p.outputWriter = writer
184 }
185
186
187
188 func (p *Progress) SetSortBy(sortBy SortBy) {
189 p.sortBy = sortBy
190 }
191
192
193 func (p *Progress) SetStyle(style Style) {
194 p.style = &style
195 }
196
197
198 func (p *Progress) SetTrackerLength(length int) {
199 p.lengthTracker = length
200 }
201
202
203
204 func (p *Progress) SetTrackerPosition(position Position) {
205 p.trackerPosition = position
206 }
207
208
209
210
211 func (p *Progress) SetUpdateFrequency(frequency time.Duration) {
212 p.updateFrequency = frequency
213 }
214
215
216
217 func (p *Progress) ShowETA(show bool) {
218 p.Style().Visibility.ETA = show
219 }
220
221
222
223 func (p *Progress) ShowPercentage(show bool) {
224 p.Style().Visibility.Percentage = show
225 }
226
227
228
229 func (p *Progress) ShowOverallTracker(show bool) {
230 p.Style().Visibility.TrackerOverall = show
231 }
232
233
234
235 func (p *Progress) ShowTime(show bool) {
236 p.Style().Visibility.Time = show
237 }
238
239
240
241 func (p *Progress) ShowTracker(show bool) {
242 p.Style().Visibility.Tracker = show
243 }
244
245
246
247 func (p *Progress) ShowValue(show bool) {
248 p.Style().Visibility.Value = show
249 }
250
251
252 func (p *Progress) Stop() {
253 if p.IsRenderInProgress() {
254 p.done <- true
255 }
256 }
257
258
259 func (p *Progress) Style() *Style {
260 if p.style == nil {
261 tempStyle := StyleDefault
262 p.style = &tempStyle
263 }
264 return p.style
265 }
266
267 func (p *Progress) initForRender() {
268
269 p.Style()
270 if p.style.Options.SpeedOverallFormatter == nil {
271 p.style.Options.SpeedOverallFormatter = FormatNumber
272 }
273
274
275 p.done = make(chan bool, 1)
276
277
278 if p.lengthTracker <= 0 {
279 p.lengthTracker = DefaultLengthTracker
280 }
281
282
283
284 p.lengthProgress = p.lengthTracker -
285 utf8.RuneCountInString(p.style.Chars.BoxLeft) -
286 utf8.RuneCountInString(p.style.Chars.BoxRight)
287 p.lengthProgressOverall = p.messageWidth +
288 text.RuneWidthWithoutEscSequences(p.style.Options.Separator) +
289 p.lengthProgress + 1
290 if p.style.Visibility.Percentage {
291 p.lengthProgressOverall += text.RuneWidthWithoutEscSequences(fmt.Sprintf(p.style.Options.PercentFormat, 0.0))
292 }
293
294
295 if p.outputWriter == nil {
296 p.outputWriter = os.Stdout
297 }
298
299
300 if p.updateFrequency <= 0 {
301 p.updateFrequency = DefaultUpdateFrequency
302 }
303 }
304
305
306 type renderHint struct {
307 hideTime bool
308 hideValue bool
309 isOverallTracker bool
310 }
311
View as plain text