github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/runners/artifacts/progress.go (about)

     1  package artifacts
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  
     8  	"github.com/ActiveState/cli/internal/constants"
     9  	"github.com/ActiveState/cli/internal/locale"
    10  	"github.com/ActiveState/cli/internal/output"
    11  	"github.com/vbauerster/mpb/v7"
    12  	"github.com/vbauerster/mpb/v7/decor"
    13  )
    14  
    15  type downloadProgress interface {
    16  	Start(size int64)
    17  	Inc(inc int)
    18  	Abort()
    19  	Stop()
    20  }
    21  
    22  type interactiveProgress struct {
    23  	pg           *mpb.Progress
    24  	bar          *mpb.Bar
    25  	artifactName string
    26  	artifactSize int
    27  	out          output.Outputer
    28  }
    29  
    30  func newDownloadProgress(ctx context.Context, out output.Outputer, artifactName, downloadPath string) downloadProgress {
    31  	if !out.Config().Interactive {
    32  		return &nonInteractiveProgress{
    33  			artifactName: artifactName,
    34  			downloadPath: downloadPath,
    35  			out:          out,
    36  		}
    37  	}
    38  
    39  	var w io.Writer = os.Stdout
    40  	if out.Type() != output.PlainFormatName {
    41  		w = nil
    42  	}
    43  	pg := mpb.NewWithContext(
    44  		ctx,
    45  		mpb.WithOutput(w),
    46  		mpb.WithWidth(40),
    47  		mpb.WithRefreshRate(constants.TerminalAnimationInterval),
    48  	)
    49  
    50  	return &interactiveProgress{
    51  		pg:           pg,
    52  		artifactName: artifactName,
    53  		out:          out,
    54  	}
    55  }
    56  
    57  func (p *interactiveProgress) Start(size int64) {
    58  	p.out.Notice(locale.Tr("builds_dl_downloading", p.artifactName))
    59  	prependDecorators := []decor.Decorator{
    60  		decor.Name(locale.T("downloading")),
    61  		decor.OnComplete(
    62  			decor.Spinner(output.SpinnerFrames, decor.WCSyncSpace), "",
    63  		),
    64  		decor.CountersKiloByte("%.1f/%.1f", decor.WC{W: 17}),
    65  	}
    66  
    67  	options := []mpb.BarOption{
    68  		mpb.BarFillerClearOnComplete(),
    69  		mpb.PrependDecorators(prependDecorators...),
    70  		mpb.AppendDecorators(
    71  			decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
    72  		),
    73  	}
    74  
    75  	p.artifactSize = int(size)
    76  	p.bar = p.pg.AddBar(size, options...)
    77  }
    78  
    79  func (p *interactiveProgress) Inc(inc int) {
    80  	p.bar.IncrBy(inc)
    81  }
    82  
    83  func (p *interactiveProgress) Abort() {
    84  	p.bar.Abort(true)
    85  }
    86  
    87  func (p *interactiveProgress) Stop() {
    88  	// The download bar should be complete at this point, but if it's not, we'll
    89  	// just set it to complete and let the progress bar finish.
    90  	if !p.bar.Completed() {
    91  		p.bar.IncrBy(p.artifactSize - int(p.bar.Current()))
    92  	}
    93  	p.pg.Wait()
    94  }
    95  
    96  type nonInteractiveProgress struct {
    97  	spinner      *output.Spinner
    98  	artifactName string
    99  	downloadPath string
   100  	out          output.Outputer
   101  }
   102  
   103  func (p *nonInteractiveProgress) Start(_ int64) {
   104  	p.spinner = output.StartSpinner(p.out, locale.Tr("builds_dl_downloading", p.artifactName), constants.TerminalAnimationInterval)
   105  }
   106  
   107  func (p *nonInteractiveProgress) Inc(_ int) {}
   108  
   109  func (p *nonInteractiveProgress) Abort() {}
   110  
   111  func (p *nonInteractiveProgress) Stop() {
   112  	p.spinner.Stop(locale.Tl("msg_download_success", "[SUCCESS]Downloaded {{.V0}} to {{.V1}}[/RESET]", p.artifactName, p.downloadPath))
   113  }