Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: change some attribute about time to time.Time{} #196

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions progressbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type state struct {
isAltSaucerHead bool

lastShown time.Time
startTime time.Time
startTime time.Time // time when the progress bar start working

counterTime time.Time
counterNumSinceLast int64
Expand Down Expand Up @@ -318,7 +318,11 @@ func NewOptions(max int, options ...Option) *ProgressBar {
// NewOptions64 constructs a new instance of ProgressBar, with any options you specify
func NewOptions64(max int64, options ...Option) *ProgressBar {
b := ProgressBar{
state: getBasicState(),
state: state{
startTime: time.Time{},
lastShown: time.Time{},
counterTime: time.Time{},
},
config: config{
writer: os.Stdout,
theme: defaultTheme,
Expand Down Expand Up @@ -486,6 +490,24 @@ func (p *ProgressBar) RenderBlank() error {
return p.render()
}

// StartWithoutRender will start the progress bar without rendering it
// this method is created for the use case where you want to start the progress
// but don't want to render it immediately.
// If you want to start the progress and render it immediately, use RenderBlank instead,
// or maybe you can use Add to start it automatically, but it will make the time calculation less precise.
func (p *ProgressBar) StartWithoutRender() {
p.lock.Lock()
defer p.lock.Unlock()

if p.IsStarted() {
return
}

p.state.startTime = time.Now()
// the counterTime should be set to the current time
p.state.counterTime = time.Now()
}

// Reset will reset the clock that is used
// to calculate current time and the time left.
func (p *ProgressBar) Reset() {
Expand Down Expand Up @@ -567,6 +589,10 @@ func (p *ProgressBar) Add64(num int64) error {

p.state.currentBytes += float64(num)

if p.state.counterTime.IsZero() {
p.state.counterTime = time.Now()
}

// reset the countdown timer every second to take rolling average
p.state.counterNumSinceLast += num
if time.Since(p.state.counterTime).Seconds() > 0.5 {
Expand Down Expand Up @@ -669,13 +695,20 @@ func (p *ProgressBar) IsFinished() bool {
return p.state.finished
}

// IsStarted returns true if progress bar is started
func (p *ProgressBar) IsStarted() bool {
return !p.state.startTime.IsZero()
}

// render renders the progress bar, updating the maximum
// rendered line width. this function is not thread-safe,
// so it must be called with an acquired lock.
func (p *ProgressBar) render() error {
// make sure that the rendering is not happening too quickly
// but always show if the currentNum reaches the max
if time.Since(p.state.lastShown).Nanoseconds() < p.config.throttleDuration.Nanoseconds() &&
if !p.IsStarted() {
p.state.startTime = time.Now()
} else if time.Since(p.state.lastShown).Nanoseconds() < p.config.throttleDuration.Nanoseconds() &&
p.state.currentNum < p.config.max {
return nil
}
Expand Down Expand Up @@ -738,7 +771,12 @@ func (p *ProgressBar) State() State {
}
s.CurrentPercent = float64(p.state.currentNum) / float64(p.config.max)
s.CurrentBytes = p.state.currentBytes
s.SecondsSince = time.Since(p.state.startTime).Seconds()
if p.IsStarted() {
s.SecondsSince = time.Since(p.state.startTime).Seconds()
} else {
s.SecondsSince = 0
}

if p.state.currentNum > 0 {
s.SecondsLeft = s.SecondsSince / float64(p.state.currentNum) * (float64(p.config.max) - float64(p.state.currentNum))
}
Expand Down
16 changes: 16 additions & 0 deletions progressbar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ func TestBarSmallBytes(t *testing.T) {
func TestBarFastBytes(t *testing.T) {
buf := strings.Builder{}
bar := NewOptions64(1e8, OptionShowBytes(true), OptionShowCount(), OptionSetWidth(10), OptionSetWriter(&buf))
bar.StartWithoutRender()
time.Sleep(time.Millisecond)
bar.Add(1e7)
if !strings.Contains(buf.String(), " GB/s)") {
Expand Down Expand Up @@ -889,6 +890,7 @@ func TestOptionFullWidth(t *testing.T) {
t.Parallel()
buf := strings.Builder{}
bar := NewOptions(100, append(test.opts, []Option{OptionFullWidth(), OptionSetWriter(&buf)}...)...)
bar.StartWithoutRender()
time.Sleep(1 * time.Second)
bar.Add(10)
time.Sleep(1 * time.Second)
Expand All @@ -913,3 +915,17 @@ func TestHumanizeBytesIEC(t *testing.T) {
amount, suffix = humanizeBytes(float64(56.78)*1024*1024*1024, true)
assert.Equal(t, "57 GiB", fmt.Sprintf("%s%s", amount, suffix))
}

func TestProgressBar_StartWithoutRender(t *testing.T) {
buf := strings.Builder{}
bar := NewOptions(100, OptionSetWriter(&buf))
time.Sleep(1 * time.Second)
bar.StartWithoutRender()
time.Sleep(1 * time.Second)
bar.Add(10)
result := strings.TrimSpace(buf.String())
expect := "10% |████ | [1s:9s]"
if result != expect {
t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
}
}
Loading