WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
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
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,8 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
64 changes: 41 additions & 23 deletions progressbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
"log"
"math"
"net/http"
"os"
Expand Down Expand Up @@ -449,17 +448,15 @@ func NewOptions64(max int64, options ...Option) *ProgressBar {
go func() {
ticker := time.NewTicker(b.config.spinnerChangeInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if b.IsFinished() {
return
}
if b.IsStarted() {
b.lock.Lock()
b.render()
b.lock.Unlock()
}

for range ticker.C {
if b.IsFinished() {
return
}
if b.IsStarted() {
b.lock.Lock()
b.render()
b.lock.Unlock()
}
}
}()
Expand Down Expand Up @@ -1014,27 +1011,48 @@ func (p *ProgressBar) State() State {

// StartHTTPServer starts an HTTP server dedicated to serving progress bar updates. This allows you to
// display the status in various UI elements, such as an OS status bar with an `xbar` extension.
// It is recommended to run this function in a separate goroutine to avoid blocking the main thread.
// When the progress bar is finished, call `server.Shutdown()` or `server.Close()` to shut it down manually.
//
// hostPort specifies the address and port to bind the server to, for example, "0.0.0.0:19999".
func (p *ProgressBar) StartHTTPServer(hostPort string) {
// for advanced users, we can return the data as json
http.HandleFunc("/state", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/json")
// since the state is a simple struct, we can just ignore the error
func (p *ProgressBar) StartHTTPServer(hostPort string) *http.Server {
mux := http.NewServeMux()

// register routes
mux.HandleFunc("/state", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
bs, _ := json.Marshal(p.State())
w.Write(bs)
})
// for others, we just return the description in a plain text format
http.HandleFunc("/desc", func(w http.ResponseWriter, r *http.Request) {

mux.HandleFunc("/desc", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
state := p.State()
fmt.Fprintf(w,
"%d/%d, %.2f%%, %s left",
p.State().CurrentNum, p.State().Max, p.State().CurrentPercent*100,
(time.Second * time.Duration(p.State().SecondsLeft)).String(),
state.CurrentNum, state.Max, state.CurrentPercent*100,
(time.Second * time.Duration(state.SecondsLeft)).String(),
)
})
log.Fatal(http.ListenAndServe(hostPort, nil))

// create the server instance
server := &http.Server{
Addr: hostPort,
Handler: mux,
}

// start the server in a goroutine and ignore errors
go func() {
defer func() {
if err := recover(); err != nil {
fmt.Println("encounter panic: ", err)
}
}()

_ = server.ListenAndServe()
}()

// return the server instance for use by the caller
return server
}

// regex matching ansi escape codes
Expand Down
28 changes: 22 additions & 6 deletions progressbar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package progressbar

import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -471,7 +472,7 @@ func TestOptionSetTheme(t *testing.T) {
bar.RenderBlank()
result := strings.TrimSpace(buf.String())
expect := "0% >----------<"
if strings.Index(result, expect) == -1 {
if !strings.Contains(result, expect) {
t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
}
buf.Reset()
Expand All @@ -487,7 +488,7 @@ func TestOptionSetTheme(t *testing.T) {
bar.Finish()
result = strings.TrimSpace(buf.String())
expect = "100% >##########<"
if strings.Index(result, expect) == -1 {
if !strings.Contains(result, expect) {
t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
}
}
Expand All @@ -506,7 +507,7 @@ func TestOptionSetThemeFilled(t *testing.T) {
bar.RenderBlank()
result := strings.TrimSpace(buf.String())
expect := "0% >----------<"
if strings.Index(result, expect) == -1 {
if !strings.Contains(result, expect) {
t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
}
buf.Reset()
Expand All @@ -522,7 +523,7 @@ func TestOptionSetThemeFilled(t *testing.T) {
bar.Finish()
result = strings.TrimSpace(buf.String())
expect = "100% ]##########["
if strings.Index(result, expect) == -1 {
if !strings.Contains(result, expect) {
t.Errorf("Render miss-match\nResult: '%s'\nExpect: '%s'\n%+v", result, expect, bar)
}
}
Expand Down Expand Up @@ -1067,7 +1068,7 @@ func TestOptionSetSpinnerChangeIntervalZero(t *testing.T) {
bar.lock.Lock()
s, _ := vt.String()
bar.lock.Unlock()
s = strings.TrimSpace(s)
_ = strings.TrimSpace(s)
}
for i := range actuals {
assert.Equal(t, expected[i], actuals[i])
Expand Down Expand Up @@ -1130,7 +1131,7 @@ func TestStartHTTPServer(t *testing.T) {
bar.Add(1)

hostPort := "localhost:9696"
go bar.StartHTTPServer(hostPort)
svr := bar.StartHTTPServer(hostPort)

// check plain text
resp, err := http.Get(fmt.Sprintf("http://%s/desc", hostPort))
Expand Down Expand Up @@ -1162,4 +1163,19 @@ func TestStartHTTPServer(t *testing.T) {
if result.Max != bar.State().Max || result.CurrentNum != bar.State().CurrentNum {
t.Errorf("wrong state: %v", result)
}

// shutdown server
err = svr.Shutdown(context.Background())
if err != nil {
t.Errorf("shutdown server failed: %v", err)
}

// start new bar server
bar = Default(10, "test")
bar.Add(1)
svr = bar.StartHTTPServer(hostPort)
err = svr.Close()
if err != nil {
t.Errorf("shutdown server failed: %v", err)
}
}
Loading