pb.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package main
  2. import (
  3. "fmt"
  4. "math"
  5. "strings"
  6. "sync/atomic"
  7. "time"
  8. )
  9. const (
  10. DEFAULT_REFRESH_RATE = time.Millisecond * 200
  11. FORMAT = "[=>-]"
  12. )
  13. type ProgressBar struct {
  14. current int64
  15. Total int64
  16. RefreshRate time.Duration
  17. ShowPercent, ShowCounters bool
  18. ShowSpeed, ShowTimeLeft, ShowBar bool
  19. ShowFinalTime bool
  20. isFinish int32
  21. startTime time.Time
  22. currentValue int64
  23. BarStart string
  24. BarEnd string
  25. Empty string
  26. Current string
  27. CurrentN string
  28. }
  29. func NewProgressBar(total int64) (pb *ProgressBar) {
  30. pb = &ProgressBar{
  31. Total: total,
  32. RefreshRate: DEFAULT_REFRESH_RATE,
  33. ShowPercent: true,
  34. ShowBar: true,
  35. ShowCounters: true,
  36. ShowFinalTime: true,
  37. ShowTimeLeft: true,
  38. ShowSpeed: true,
  39. BarStart: "[",
  40. BarEnd: "]",
  41. Empty: "_",
  42. Current: "=",
  43. CurrentN: ">",
  44. }
  45. return
  46. }
  47. func (pb *ProgressBar) Start() {
  48. pb.startTime = time.Now()
  49. if pb.Total == 0 {
  50. pb.ShowBar = false
  51. pb.ShowTimeLeft = false
  52. pb.ShowPercent = false
  53. }
  54. go pb.writer()
  55. }
  56. // Write the current state of the progressbar
  57. func (pb *ProgressBar) Update() {
  58. c := atomic.LoadInt64(&pb.current)
  59. if c != pb.currentValue {
  60. pb.write(c)
  61. pb.currentValue = c
  62. }
  63. }
  64. // Internal loop for writing progressbar
  65. func (pb *ProgressBar) writer() {
  66. for {
  67. if atomic.LoadInt32(&pb.isFinish) != 0 {
  68. break
  69. }
  70. pb.Update()
  71. time.Sleep(pb.RefreshRate)
  72. }
  73. }
  74. // Increment current value
  75. func (pb *ProgressBar) Increment() int {
  76. return pb.Add(1)
  77. }
  78. // Set current value
  79. func (pb *ProgressBar) Set(current int) {
  80. atomic.StoreInt64(&pb.current, int64(current))
  81. }
  82. // Add to current value
  83. func (pb *ProgressBar) Add(add int) int {
  84. return int(pb.Add64(int64(add)))
  85. }
  86. func (pb *ProgressBar) Add64(add int64) int64 {
  87. return atomic.AddInt64(&pb.current, add)
  88. }
  89. // End print
  90. func (pb *ProgressBar) Finish() {
  91. atomic.StoreInt32(&pb.isFinish, 1)
  92. pb.write(atomic.LoadInt64(&pb.current))
  93. }
  94. // implement io.Writer
  95. func (pb *ProgressBar) Write(p []byte) (n int, err error) {
  96. n = len(p)
  97. pb.Add(n)
  98. return
  99. }
  100. func (pb *ProgressBar) write(current int64) {
  101. width := 123
  102. var percentBox, countersBox, timeLeftBox, speedBox, barBox, end, out string
  103. // percents
  104. if pb.ShowPercent {
  105. percent := float64(current) / (float64(pb.Total) / float64(100))
  106. percentBox = fmt.Sprintf(" %#.02f %% ", percent)
  107. }
  108. // counters
  109. if pb.ShowCounters {
  110. if pb.Total > 0 {
  111. countersBox = fmt.Sprintf("%s / %s ", FormatBytes(current), FormatBytes(pb.Total))
  112. } else {
  113. countersBox = FormatBytes(current) + " "
  114. }
  115. }
  116. // time left
  117. fromStart := time.Now().Sub(pb.startTime)
  118. if atomic.LoadInt32(&pb.isFinish) != 0 {
  119. if pb.ShowFinalTime {
  120. left := (fromStart / time.Second) * time.Second
  121. timeLeftBox = left.String()
  122. }
  123. } else if pb.ShowTimeLeft && current > 0 {
  124. perEntry := fromStart / time.Duration(current)
  125. left := time.Duration(pb.Total-current) * perEntry
  126. left = (left / time.Second) * time.Second
  127. timeLeftBox = left.String()
  128. }
  129. // speed
  130. if pb.ShowSpeed && current > 0 {
  131. fromStart := time.Now().Sub(pb.startTime)
  132. speed := float64(current) / (float64(fromStart) / float64(time.Second))
  133. speedBox = FormatBytes(int64(speed)) + "/s "
  134. }
  135. // bar
  136. if pb.ShowBar {
  137. size := width - len(countersBox+pb.BarStart+pb.BarEnd+percentBox+timeLeftBox+speedBox)
  138. if size > 0 {
  139. curCount := int(math.Ceil((float64(current) / float64(pb.Total)) * float64(size)))
  140. emptCount := size - curCount
  141. barBox = pb.BarStart
  142. if emptCount < 0 {
  143. emptCount = 0
  144. }
  145. if curCount > size {
  146. curCount = size
  147. }
  148. if emptCount <= 0 {
  149. barBox += strings.Repeat(pb.Current, curCount)
  150. } else if curCount > 0 {
  151. barBox += strings.Repeat(pb.Current, curCount-1) + pb.CurrentN
  152. }
  153. barBox += strings.Repeat(pb.Empty, emptCount) + pb.BarEnd
  154. }
  155. }
  156. // check len
  157. out = countersBox + barBox + percentBox + speedBox + timeLeftBox
  158. if len(out) < width {
  159. end = strings.Repeat(" ", width-len(out))
  160. }
  161. // and print!
  162. fmt.Print("\r" + out + end)
  163. }