...

Source file src/github.com/redhat-developer/odo/pkg/log/fidget/spinner.go

Documentation: github.com/redhat-developer/odo/pkg/log/fidget

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  /*
    18  	This package is a FORK of https://github.com/kubernetes-sigs/kind/blob/master/pkg/log/status.go
    19  	See above license
    20  */
    21  
    22  // Package fidget implements CLI functionality for bored users waiting for results
    23  package fidget
    24  
    25  import (
    26  	"fmt"
    27  	"io"
    28  	"runtime"
    29  	"sync"
    30  	"time"
    31  )
    32  
    33  // These are frames for the default "spinner" using unicode. These
    34  // are meant for macOS and Linux terminals that by default support unicode.
    35  var unicodeSpinnerFrames = []string{
    36  	"◐",
    37  	"◓",
    38  	"◑",
    39  	"◒",
    40  }
    41  
    42  // These are the spinner using ASCII. We revert to these frames
    43  // for Windows terminals that don't support unicode.
    44  var asciiSpinnerFrames = []string{
    45  	"<",
    46  	"^",
    47  	">",
    48  	"v",
    49  }
    50  
    51  // Spinner is a simple and efficient CLI loading spinner used by kind
    52  // It is simplistic and assumes that the line length will not change.
    53  // It is best used indirectly via log.Status (see parent package)
    54  type Spinner struct {
    55  	frames []string
    56  	stop   chan struct{}
    57  	ticker *time.Ticker
    58  	writer io.Writer
    59  	mu     *sync.Mutex
    60  	// protected by mu
    61  	prefix string
    62  	suffix string
    63  	start  time.Time
    64  }
    65  
    66  // NewSpinner initializes and returns a new Spinner that will write to
    67  func NewSpinner(w io.Writer) *Spinner {
    68  
    69  	frames := unicodeSpinnerFrames
    70  	if runtime.GOOS == "windows" {
    71  		frames = asciiSpinnerFrames
    72  	}
    73  
    74  	return &Spinner{
    75  		frames: frames,
    76  		stop:   make(chan struct{}, 1),
    77  		ticker: time.NewTicker(time.Millisecond * 200),
    78  		mu:     &sync.Mutex{},
    79  		writer: w,
    80  		start:  time.Now(),
    81  	}
    82  }
    83  
    84  // SetPrefix sets the prefix to print before the spinner
    85  func (s *Spinner) SetPrefix(prefix string) {
    86  	s.mu.Lock()
    87  	s.prefix = prefix
    88  	s.mu.Unlock()
    89  }
    90  
    91  // SetSuffix sets the suffix to print after the spinner
    92  func (s *Spinner) SetSuffix(suffix string) {
    93  	s.mu.Lock()
    94  
    95  	// Awful hack to "clear" the line if the line is better than the previous one...
    96  	if len(suffix) < len(s.suffix) {
    97  		spacingLength := len(s.prefix) + len(s.suffix)
    98  		fmt.Fprintf(s.writer, "\r%*s", spacingLength, "")
    99  	}
   100  	s.suffix = suffix
   101  
   102  	// Make sure we go back to the original line...
   103  	fmt.Print("\r")
   104  	s.mu.Unlock()
   105  }
   106  
   107  // Start starts the spinner running
   108  func (s *Spinner) Start() {
   109  	go func() {
   110  		for {
   111  			for _, frame := range s.frames {
   112  				select {
   113  				case <-s.stop:
   114  					return
   115  				case <-s.ticker.C:
   116  					func() {
   117  						s.mu.Lock()
   118  						defer s.mu.Unlock()
   119  						fmt.Fprintf(s.writer, "\r%s%s%s", s.prefix, frame, s.suffix)
   120  					}()
   121  				}
   122  			}
   123  		}
   124  	}()
   125  }
   126  
   127  // Stop signals the spinner to stop
   128  func (s *Spinner) Stop() {
   129  	s.stop <- struct{}{}
   130  }
   131  
   132  // TimeSpent returns the seconds spent since the spinner first started
   133  func (s *Spinner) TimeSpent() string {
   134  	currentTime := time.Now()
   135  	timeElapsed := currentTime.Sub(s.start)
   136  
   137  	// Print ms if less than a second
   138  	// else print out minutes if more than 1 minute
   139  	// else print the default (seconds)
   140  	if timeElapsed > time.Minute {
   141  		return fmt.Sprintf("%.0fm", timeElapsed.Minutes())
   142  	} else if timeElapsed < time.Minute && timeElapsed > time.Second {
   143  		return fmt.Sprintf("%.0fs", timeElapsed.Seconds())
   144  	} else if timeElapsed < time.Second && timeElapsed > time.Millisecond {
   145  		return fmt.Sprintf("%dms", timeElapsed.Nanoseconds()/int64(time.Millisecond))
   146  	}
   147  
   148  	// If it's less than 1ms (nanoseconds), output none.
   149  	return ""
   150  }
   151  

View as plain text