M main.go => main.go +2 -2
@@ 20,7 20,7 @@ func main() {
fmt.Println(strings.Repeat("=", 20))
var p progress.Progress
p.Width = 30
- p.Style = "trapez"
+ p.SetStyle("trapez")
for i := 0; i < 350; i++ {
perc, _ := progress.GetPercentage(float64(i), 350)
@@ 44,7 44,7 @@ func main() {
fmt.Println()
fmt.Println(strings.Repeat("=", 20))
p.Width = 20
- p.Style = "block"
+ p.SetStyle("block")
s.SetStyle("braille")
for i := 0; i < 450; i++ {
perc, _ := progress.GetPercentage(float64(i-100), 350)
M progress/calculate.go => progress/calculate.go +1 -0
@@ 2,6 2,7 @@ package progress
import "errors"
+// GetPercentage returns the percentage of a part from a total.
func GetPercentage(parts, total float64) (float64, error) {
if total == 0 {
return 0, errors.New("division by zero")
M progress/styles.go => progress/styles.go +17 -0
@@ 1,5 1,9 @@
package progress
+import "sync"
+
+// ProgressStyles contains the styles of the progressbars. Custom styles can
+// easily be appended and used.
var ProgressStyles = map[string][]string{
"double": []string{" ", "="},
"double-": []string{" ", "-", "="},
@@ 9,3 13,16 @@ var ProgressStyles = map[string][]string{
"block": []string{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"},
"": []string{" ", "="},
}
+
+// progressStyleMtx locks the map so there are no concurrent map-accesses
+var progressStyleMtx sync.RWMutex
+
+// DefineStyle allows registering a custom progress-style to use. If the name
+// is already defined it is overwritten. Directly defining it is possible but
+// not recommended.
+func DefineStyle(name string, characters []string) {
+ progressStyleMtx.Lock()
+ defer progressStyleMtx.Unlock()
+
+ ProgressStyles[name] = characters
+}
M progress/type.go => progress/type.go +34 -7
@@ 1,25 1,52 @@
package progress
import (
+ "errors"
"math"
"strings"
"sync"
)
+// Progress contains the settings of a Progressbar.
type Progress struct {
- Style string
+ style string
Width int
- mtx sync.Mutex
+ prgChars []string
+ prgCharsMtx sync.RWMutex
}
+// SetStyle sets the style that should be used with the progressbar
+func (p *Progress) SetStyle(style string) error {
+ if style == p.style {
+ return nil
+ }
+ progressStyleMtx.RLock()
+ defer progressStyleMtx.RUnlock()
+
+ prgc, exists := ProgressStyles[style]
+ if !exists {
+ return errors.New("undefined style")
+ }
+
+ p.prgCharsMtx.Lock()
+ p.prgChars = prgc
+ p.prgCharsMtx.Unlock()
+ p.style = style
+ return nil
+}
+
+// GetBar returns the string that represents the progressbar in the set style
+// for the given progress
func (p *Progress) GetBar(parts, total float64) (result string) {
if p.Width <= 0 {
p.Width = 10
}
- maxIndex := len(ProgressStyles[p.Style]) - 1
+ p.prgCharsMtx.RLock()
+ defer p.prgCharsMtx.RUnlock()
+ maxIndex := len(p.prgChars) - 1
if total == 0 {
- result += strings.Repeat(ProgressStyles[p.Style][maxIndex], p.Width)
+ result += strings.Repeat(p.prgChars[maxIndex], p.Width)
return
}
@@ 34,7 61,7 @@ func (p *Progress) GetBar(parts, total float64) (result string) {
}
for percent > percperchar {
- result += ProgressStyles[p.Style][maxIndex]
+ result += p.prgChars[maxIndex]
percent -= percperchar
counter++
@@ 57,12 84,12 @@ func (p *Progress) GetBar(parts, total float64) (result string) {
if charindex > maxIndex {
charindex = maxIndex
}
- result += ProgressStyles[p.Style][charindex]
+ result += p.prgChars[charindex]
counter++
}
for counter < p.Width {
- result += ProgressStyles[p.Style][0]
+ result += p.prgChars[0]
counter++
}
return
M spinner/styles.go => spinner/styles.go +16 -0
@@ 1,5 1,8 @@
package spinner
+import "sync"
+
+// SpinnerStyles contains the styles of spinners
var SpinnerStyles = map[string][]string{
"braille": []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
"braille-thin": []string{"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
@@ 17,3 20,16 @@ var SpinnerStyles = map[string][]string{
"clock": []string{"🕛 ", "🕐 ", "🕑 ", "🕒 ", "🕓 ", "🕔 ", "🕕 ", "🕖 ", "🕗 ", "🕘 ", "🕙 ", "🕚 "},
"": []string{"|", "/", "-", "\\"},
}
+
+// spinnerStyleMtx locks the map so there are no concurrent map-accesses
+var spinnerStyleMtx sync.RWMutex
+
+// DefineStyle allows registering a custom spinner-style to use. If the name is
+// already defined it is overwritten. Directly defining them is possible but
+// not recommended.
+func DefineStyle(name string, characters []string) {
+ spinnerStyleMtx.Lock()
+ defer spinnerStyleMtx.Unlock()
+
+ SpinnerStyles[name] = characters
+}
M spinner/type.go => spinner/type.go +39 -9
@@ 6,40 6,70 @@ import (
"unicode/utf8"
)
+// Spinner contains the settings of a spinner
type Spinner struct {
style string
currentIndex int
- mtx sync.Mutex
+ spinChars []string
+ spinCharsMtx sync.RWMutex
}
+// Next returns the next character for the spinner
func (s *Spinner) Next() string {
- s.mtx.Lock()
+ s.loadIfUnloaded()
+ s.spinCharsMtx.RLock()
s.currentIndex++
- if s.currentIndex >= len(SpinnerStyles[s.style]) {
+ if s.currentIndex >= len(s.spinChars) {
s.currentIndex = 0
}
- s.mtx.Unlock()
+ s.spinCharsMtx.RUnlock()
return s.Current()
}
+// Current returns the current character for the spinner
func (s *Spinner) Current() string {
- s.mtx.Lock()
- if s.currentIndex >= len(SpinnerStyles[s.style]) {
+ s.loadIfUnloaded()
+ s.spinCharsMtx.RLock()
+ defer s.spinCharsMtx.RUnlock()
+ // just in case someone changed the style mid-way
+ if s.currentIndex >= len(s.spinChars) {
s.currentIndex = 0
}
- s.mtx.Unlock()
return SpinnerStyles[s.style][s.currentIndex]
}
+// SetStyle loads a style into the spinner
func (s *Spinner) SetStyle(style string) {
- s.mtx.Lock()
- defer s.mtx.Unlock()
+ if style == s.style {
+ return
+ }
+ s.spinCharsMtx.Lock()
+ defer s.spinCharsMtx.Unlock()
s.style = style
+ spinnerStyleMtx.RLock()
+ s.spinChars = SpinnerStyles[s.style]
+ spinnerStyleMtx.RUnlock()
}
+// Clear returns the amount of characters for the first spinner-state in spaces
+// in order to clear the spinner if required.
func (s *Spinner) Clear() string {
return strings.Repeat(" ", utf8.RuneCountInString(SpinnerStyles[s.style][0]))
}
+
+// loadIfUnloaded makes sure a style is always loaded.
+func (s *Spinner) loadIfUnloaded() {
+ s.spinCharsMtx.Lock()
+ if len(s.spinChars) > 0 {
+ s.spinCharsMtx.Unlock()
+ return
+ }
+
+ spinnerStyleMtx.RLock()
+ s.spinChars = SpinnerStyles[""]
+ s.spinCharsMtx.Unlock()
+ spinnerStyleMtx.RUnlock()
+}