~poldi1405/go-indicators

71947156e3bfa460e0005fa9fdb10871d058ebd8 — Moritz Poldrack 1 year, 9 months ago 416a589
added documentation
6 files changed, 109 insertions(+), 18 deletions(-)

M main.go
M progress/calculate.go
M progress/styles.go
M progress/type.go
M spinner/styles.go
M spinner/type.go
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()
}