Skip to content

Instantly share code, notes, and snippets.

@mlanin
Created February 14, 2022 07:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mlanin/fab7a5610f0d2ce481688da1af134dab to your computer and use it in GitHub Desktop.
Save mlanin/fab7a5610f0d2ce481688da1af134dab to your computer and use it in GitHub Desktop.
Create a nice progress bar using symbols
package progress_bar
import (
"strings"
"text/template"
)
// Options can be used to customize look of the progress bar. DefaultProgressOptions() has pretty good defaults.
type Options struct {
Fill string // The character(s) used to fill in the progress bar
Empty string // The character(s) used to indicate empty space at the end of progress bar
Width int // How many characters wide the progress bar should be. A value of 10 looks good on slack phone clients.
Msg *template.Template // The message template that will be sent to slack. Uses text/template for creating templates.
}
var defaultTemplate = template.Must(template.New("default_progress_msg").Parse("`{{.ProgBar}}` {{.Done}}% ({{.Pos}}/{{.Total}})"))
// DefaultProgressOptions creates an Options struct with decent defaults.
func DefaultProgressOptions() *Options {
return &Options{
Fill: "🟦",
Empty: "⬜",
Width: 10, // Looks good on slack phone clients
Msg: defaultTemplate,
}
}
type Progress struct {
Opts *Options
}
// NewProgress creates a new progress bar. Uses default options if non were set
func NewProgress(opts *Options) *Progress {
progress := &Progress{
Opts: opts,
}
if opts == nil {
progress.Opts = DefaultProgressOptions()
}
return progress
}
func (p *Progress) drawBar(pos int, total int) string {
if pos == 0 || total == 0 {
return strings.Repeat(p.Opts.Empty, p.Opts.Width)
}
done := pos * 100 / total
bar := strings.Repeat(p.Opts.Fill, done/p.Opts.Width)
bar += strings.Repeat(p.Opts.Empty, p.Opts.Width-len([]rune(bar)))
return bar
}
func (p *Progress) GetProgress(pos int, total int) (string, error) {
msg := &strings.Builder{}
done := 0
if total != 0 {
done = pos * 100 / total
}
data := map[string]interface{}{
"ProgBar": p.drawBar(pos, total),
"Done": done,
"Pos": pos,
"Total": total,
"Complete": pos == total,
}
err := p.Opts.Msg.Execute(msg, data)
return msg.String(), err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment