Skip to content

Commit

Permalink
pacman and dominoes progress bars
Browse files Browse the repository at this point in the history
  • Loading branch information
jedib0t committed Jan 29, 2021
1 parent ee1b017 commit 0f05093
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 16 deletions.
117 changes: 113 additions & 4 deletions progress/indicator.go
@@ -1,6 +1,7 @@
package progress

import (
"strings"
"time"

"github.com/jedib0t/go-pretty/v6/text"
Expand All @@ -17,6 +18,26 @@ type IndeterminateIndicator struct {
// where the progress percentage cannot be calculated. Ex.: [........<=>....]
type IndeterminateIndicatorGenerator func(maxLen int) IndeterminateIndicator

// IndeterminateIndicatorDominoes returns an instance of
// IndeterminateIndicatorGenerator function that simulates a bunch of dominoes
// falling.
func IndeterminateIndicatorDominoes(duration time.Duration) IndeterminateIndicatorGenerator {
var indeterminateIndicator *IndeterminateIndicator
indicatorGenerator := indeterminateIndicatorDominoes()
lastRenderTime := time.Now()

return func(maxLen int) IndeterminateIndicator {
currRenderTime := time.Now()
if indeterminateIndicator == nil || duration == 0 || currRenderTime.Sub(lastRenderTime) > duration {
tmpIndeterminateIndicator := indicatorGenerator(maxLen)
indeterminateIndicator = &tmpIndeterminateIndicator
lastRenderTime = currRenderTime
}

return *indeterminateIndicator
}
}

// IndeterminateIndicatorMovingBackAndForth returns an instance of
// IndeterminateIndicatorGenerator function that incrementally moves from the
// left to right and back for each specified duration. If duration is 0, then
Expand Down Expand Up @@ -80,18 +101,68 @@ func IndeterminateIndicatorMovingRightToLeft(indicator string, duration time.Dur
}
}

// IndeterminateIndicatorPacMan returns an instance of
// IndeterminateIndicatorGenerator function that simulates a Pac-Man character
// chomping through the progress bar.
func IndeterminateIndicatorPacMan(duration time.Duration) IndeterminateIndicatorGenerator {
var indeterminateIndicator *IndeterminateIndicator
indicatorGenerator := indeterminateIndicatorPacMan()
lastRenderTime := time.Now()

return func(maxLen int) IndeterminateIndicator {
currRenderTime := time.Now()
if indeterminateIndicator == nil || duration == 0 || currRenderTime.Sub(lastRenderTime) > duration {
tmpIndeterminateIndicator := indicatorGenerator(maxLen)
indeterminateIndicator = &tmpIndeterminateIndicator
lastRenderTime = currRenderTime
}

return *indeterminateIndicator
}
}

func indeterminateIndicatorDominoes() IndeterminateIndicatorGenerator {
direction := 1 // positive == left to right; negative == right to left
nextPosition := 0

out := strings.Builder{}
generateIndicator := func(currentPosition int, maxLen int) string {
out.Reset()
out.WriteString(strings.Repeat("/", currentPosition))
out.WriteString(strings.Repeat("\\", maxLen-currentPosition))
return out.String()
}

return func(maxLen int) IndeterminateIndicator {
currentPosition := nextPosition

if currentPosition == 0 {
direction = 1
} else if currentPosition == maxLen {
direction = -1
}
nextPosition += direction

return IndeterminateIndicator{
Position: 0,
Text: generateIndicator(currentPosition, maxLen),
}
}
}

func indeterminateIndicatorMovingBackAndForth(indicator string) IndeterminateIndicatorGenerator {
increment := 1
direction := 1 // positive == left to right; negative == right to left
nextPosition := 0

return func(maxLen int) IndeterminateIndicator {
currentPosition := nextPosition

if currentPosition == 0 {
increment = 1
direction = 1
} else if currentPosition+text.RuneCount(indicator) == maxLen {
increment = -1
direction = -1
}
nextPosition += increment
nextPosition += direction

return IndeterminateIndicator{
Position: currentPosition,
Expand All @@ -105,6 +176,7 @@ func indeterminateIndicatorMovingLeftToRight(indicator string) IndeterminateIndi

return func(maxLen int) IndeterminateIndicator {
currentPosition := nextPosition

nextPosition++
if nextPosition+text.RuneCount(indicator) > maxLen {
nextPosition = 0
Expand Down Expand Up @@ -133,3 +205,40 @@ func indeterminateIndicatorMovingRightToLeft(indicator string) IndeterminateIndi
}
}
}

func indeterminateIndicatorPacMan() IndeterminateIndicatorGenerator {
pacManMovingRight, pacManMovingLeft := "ᗧ", "ᗤ"
direction := 1 // positive == left to right; negative == right to left
indicator := pacManMovingRight
nextPosition := 0

out := strings.Builder{}
generateIndicator := func(currentPosition int, maxLen int) string {
out.Reset()
if currentPosition > 0 {
out.WriteString(strings.Repeat(" ", currentPosition))
}
out.WriteString(indicator)
out.WriteString(strings.Repeat(" ", maxLen-currentPosition-1))
return out.String()
}

return func(maxLen int) IndeterminateIndicator {
currentPosition := nextPosition
currentText := generateIndicator(currentPosition, maxLen)

if currentPosition == 0 {
direction = 1
indicator = pacManMovingRight
} else if currentPosition+text.RuneCount(indicator) == maxLen {
direction = -1
indicator = pacManMovingLeft
}
nextPosition += direction

return IndeterminateIndicator{
Position: 0,
Text: currentText,
}
}
}
120 changes: 108 additions & 12 deletions progress/indicator_test.go
Expand Up @@ -2,12 +2,62 @@ package progress

import (
"fmt"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestIndeterminateIndicatorDominoes(t *testing.T) {
maxLen := 10
expectedTexts := []string{
`\\\\\\\\\\`,
`/\\\\\\\\\`,
`//\\\\\\\\`,
`///\\\\\\\`,
`////\\\\\\`,
`/////\\\\\`,
`//////\\\\`,
`///////\\\`,
`////////\\`,
`/////////\`,
`//////////`,
`/////////\`,
`////////\\`,
`///////\\\`,
`//////\\\\`,
`/////\\\\\`,
`////\\\\\\`,
`///\\\\\\\`,
`//\\\\\\\\`,
`/\\\\\\\\\`,
`\\\\\\\\\\`,
`/\\\\\\\\\`,
`//\\\\\\\\`,
`///\\\\\\\`,
`////\\\\\\`,
`/////\\\\\`,
`//////\\\\`,
`///////\\\`,
`////////\\`,
`/////////\`,
}

out := strings.Builder{}
f := IndeterminateIndicatorDominoes(time.Millisecond * 10)
for idx, expectedText := range expectedTexts {
actual := f(maxLen)
assert.Equal(t, 0, actual.Position, fmt.Sprintf("expectedTexts[%d]", idx))
assert.Equal(t, expectedText, actual.Text, fmt.Sprintf("expectedTexts[%d]", idx))
out.WriteString(fmt.Sprintf("`%v`,\n", actual.Text))
time.Sleep(time.Millisecond * 10)
}
if t.Failed() {
fmt.Println(out.String())
}
}

func TestIndeterminateIndicatorMovingBackAndForth(t *testing.T) {
maxLen := 10
indicator := "<=>"
Expand All @@ -19,7 +69,7 @@ func TestIndeterminateIndicatorMovingBackAndForth(t *testing.T) {
f := IndeterminateIndicatorMovingBackAndForth(indicator, time.Millisecond*10)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
time.Sleep(time.Millisecond * 10)
}
}
Expand All @@ -35,7 +85,7 @@ func Test_indeterminateIndicatorMovingBackAndForth1(t *testing.T) {
f := indeterminateIndicatorMovingBackAndForth(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -50,7 +100,7 @@ func Test_indeterminateIndicatorMovingBackAndForth2(t *testing.T) {
f := indeterminateIndicatorMovingBackAndForth(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -65,7 +115,7 @@ func Test_indeterminateIndicatorMovingBackAndForth3(t *testing.T) {
f := indeterminateIndicatorMovingBackAndForth(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -80,7 +130,7 @@ func TestIndeterminateIndicatorMovingLeftToRight(t *testing.T) {
f := IndeterminateIndicatorMovingLeftToRight(indicator, time.Millisecond*10)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
time.Sleep(time.Millisecond * 10)
}
}
Expand All @@ -96,7 +146,7 @@ func Test_indeterminateIndicatorMovingLeftToRight1(t *testing.T) {
f := indeterminateIndicatorMovingLeftToRight(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -111,7 +161,7 @@ func Test_indeterminateIndicatorMovingLeftToRight2(t *testing.T) {
f := indeterminateIndicatorMovingLeftToRight(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -126,7 +176,7 @@ func Test_indeterminateIndicatorMovingLeftToRight3(t *testing.T) {
f := indeterminateIndicatorMovingLeftToRight(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -141,7 +191,7 @@ func TestIndeterminateIndicatorMovingRightToLeft(t *testing.T) {
f := IndeterminateIndicatorMovingRightToLeft(indicator, time.Millisecond*10)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
time.Sleep(time.Millisecond * 10)
}
}
Expand All @@ -157,7 +207,7 @@ func Test_indeterminateIndicatorMovingRightToLeft1(t *testing.T) {
f := indeterminateIndicatorMovingRightToLeft(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -172,7 +222,7 @@ func Test_indeterminateIndicatorMovingRightToLeft2(t *testing.T) {
f := indeterminateIndicatorMovingRightToLeft(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

Expand All @@ -187,6 +237,52 @@ func Test_indeterminateIndicatorMovingRightToLeft3(t *testing.T) {
f := indeterminateIndicatorMovingRightToLeft(indicator)
for idx, expectedPosition := range expectedPositions {
actual := f(maxLen)
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedIndeterminateIndicators[%d]", idx))
assert.Equal(t, expectedPosition, actual.Position, fmt.Sprintf("expectedPositions[%d]", idx))
}
}

func TestIndeterminateIndicatorPacMan(t *testing.T) {
maxLen := 10
expectedTexts := []string{
"ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ",
" ᗤ ",
" ᗤ ",
" ᗤ ",
" ᗤ ",
" ᗤ ",
" ᗤ ",
" ᗤ ",
" ᗤ ",
"ᗤ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ ",
" ᗧ",
}

out := strings.Builder{}
f := IndeterminateIndicatorPacMan(time.Millisecond * 10)
for idx, expectedText := range expectedTexts {
actual := f(maxLen)
assert.Equal(t, expectedText, actual.Text, fmt.Sprintf("expectedTexts[%d]", idx))
out.WriteString(fmt.Sprintf("%#v,\n", actual.Text))
time.Sleep(time.Millisecond * 10)
}
if t.Failed() {
fmt.Println(out.String())
}
}

0 comments on commit 0f05093

Please sign in to comment.