github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/waitlivemsg/waitlivemsg.go (about)

     1  package waitlivemsg
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	"bytes"
     9  
    10  	"github.com/henvic/uilive"
    11  )
    12  
    13  var spinners = []string{
    14  	"⠋",
    15  	"⠙",
    16  	"⠹",
    17  	"⠸",
    18  	"⠼",
    19  	"⠴",
    20  	"⠦",
    21  	"⠧",
    22  	"⠇",
    23  	"⠏",
    24  }
    25  
    26  // Message to print
    27  type Message struct {
    28  	text    string
    29  	counter int
    30  	mutex   sync.RWMutex
    31  }
    32  
    33  // NewMessage creates a Message with a given text
    34  func NewMessage(text string) *Message {
    35  	var m = Message{}
    36  	m.PlayText(text)
    37  	return &m
    38  }
    39  
    40  // EmptyLine creates a Message with an empty line
    41  // it has the side-effect of using no symbols as prefixes
    42  func EmptyLine() *Message {
    43  	return NewMessage("")
    44  }
    45  
    46  // PlayText as a live message
    47  func (m *Message) PlayText(text string) {
    48  	m.mutex.Lock()
    49  	m.text = text
    50  	if m.counter == -1 {
    51  		m.counter = 0
    52  	}
    53  	m.mutex.Unlock()
    54  }
    55  
    56  // StopText of the live messages
    57  func (m *Message) StopText(text string) {
    58  	m.mutex.Lock()
    59  	m.text = text
    60  	m.counter = -1
    61  	m.mutex.Unlock()
    62  }
    63  
    64  // GetText of the message
    65  func (m *Message) GetText() string {
    66  	m.mutex.RLock()
    67  	var text = m.text
    68  	m.mutex.RUnlock()
    69  	return text
    70  }
    71  
    72  func (m *Message) getSymbol() string {
    73  	m.mutex.RLock()
    74  	var c = m.counter
    75  	m.mutex.RUnlock()
    76  
    77  	if c == -1 || m.text == "" {
    78  		return ""
    79  	}
    80  
    81  	var symbol = spinners[c]
    82  	m.mutex.Lock()
    83  	m.counter = (c + 1) % len(spinners)
    84  	m.mutex.Unlock()
    85  
    86  	return symbol
    87  }
    88  
    89  func (m *Message) getText() string {
    90  	m.mutex.RLock()
    91  	defer m.mutex.RUnlock()
    92  	return m.text
    93  }
    94  
    95  // End of live message
    96  func (m *Message) End() {
    97  	m.mutex.Lock()
    98  	m.counter = -1
    99  	m.mutex.Unlock()
   100  }
   101  
   102  // WaitLiveMsg is used for "waiting" live message
   103  type WaitLiveMsg struct {
   104  	msgs         []*Message
   105  	stream       *uilive.Writer
   106  	msgsMutex    sync.RWMutex
   107  	start        time.Time
   108  	tickerd      chan bool
   109  	tickerdMutex sync.RWMutex
   110  	waitEnd      sync.WaitGroup
   111  }
   112  
   113  // New creates a WaitLiveMsg
   114  func New(ws *uilive.Writer) *WaitLiveMsg {
   115  	if ws == nil {
   116  		ws = uilive.New()
   117  	}
   118  
   119  	return &WaitLiveMsg{
   120  		stream: ws,
   121  	}
   122  }
   123  
   124  // AddMessage to display
   125  func (w *WaitLiveMsg) AddMessage(msg *Message) {
   126  	w.msgsMutex.Lock()
   127  	w.msgs = append(w.msgs, msg)
   128  	w.msgsMutex.Unlock()
   129  }
   130  
   131  // SetMessage to display
   132  func (w *WaitLiveMsg) SetMessage(msg *Message) {
   133  	w.msgsMutex.Lock()
   134  	w.msgs = []*Message{msg}
   135  	w.msgsMutex.Unlock()
   136  }
   137  
   138  // SetMessages to display
   139  func (w *WaitLiveMsg) SetMessages(msgs []*Message) {
   140  	w.msgsMutex.Lock()
   141  	w.msgs = msgs
   142  	w.msgsMutex.Unlock()
   143  }
   144  
   145  // GetMessages displayed
   146  func (w *WaitLiveMsg) GetMessages() []*Message {
   147  	w.msgsMutex.RLock()
   148  	defer w.msgsMutex.RUnlock()
   149  	return w.msgs
   150  }
   151  
   152  // RemoveMessage from the messages slice
   153  func (w *WaitLiveMsg) RemoveMessage(msg *Message) {
   154  	w.msgsMutex.Lock()
   155  	var newSlice = []*Message{}
   156  	for _, m := range w.msgs {
   157  		if m != msg {
   158  			newSlice = append(newSlice, m)
   159  		}
   160  	}
   161  	w.msgs = newSlice
   162  	w.msgsMutex.Unlock()
   163  }
   164  
   165  // ResetMessages to display
   166  func (w *WaitLiveMsg) ResetMessages() {
   167  	w.msgsMutex.Lock()
   168  	w.msgs = []*Message{}
   169  	w.msgsMutex.Unlock()
   170  }
   171  
   172  // SetStream to output to
   173  func (w *WaitLiveMsg) SetStream(ws *uilive.Writer) {
   174  	w.msgsMutex.Lock()
   175  	w.stream = ws
   176  	w.msgsMutex.Unlock()
   177  }
   178  
   179  // Wait starts the waiting message
   180  func (w *WaitLiveMsg) Wait() {
   181  	w.waitEnd.Add(1)
   182  	w.tickerdMutex.Lock()
   183  	w.tickerd = make(chan bool, 1)
   184  	w.start = time.Now()
   185  	w.tickerdMutex.Unlock()
   186  
   187  	w.waitLoop()
   188  
   189  	w.print()
   190  	w.waitEnd.Done()
   191  }
   192  
   193  func (w *WaitLiveMsg) waitLoop() {
   194  	var ticker = time.NewTicker(60 * time.Millisecond)
   195  	for {
   196  		w.tickerdMutex.RLock()
   197  		select {
   198  		case <-ticker.C:
   199  			w.print()
   200  		case <-w.tickerd:
   201  			ticker.Stop()
   202  			ticker = nil
   203  			return
   204  		}
   205  		w.tickerdMutex.RUnlock()
   206  	}
   207  }
   208  
   209  // Stop the waiting message
   210  func (w *WaitLiveMsg) Stop() {
   211  	w.msgsMutex.RLock()
   212  	for _, m := range w.msgs {
   213  		m.mutex.RLock()
   214  		c := m.counter
   215  		m.mutex.RUnlock()
   216  		if c != -1 {
   217  			m.End()
   218  		}
   219  	}
   220  	w.msgsMutex.RUnlock()
   221  	w.tickerdMutex.Lock()
   222  
   223  	if w.tickerd != nil {
   224  		w.tickerd <- true
   225  	}
   226  
   227  	w.tickerdMutex.Unlock()
   228  	w.waitEnd.Wait()
   229  }
   230  
   231  // ResetDuration to restart counter
   232  func (w *WaitLiveMsg) ResetDuration() {
   233  	w.tickerdMutex.RLock()
   234  	w.start = time.Now()
   235  	w.tickerdMutex.RUnlock()
   236  }
   237  
   238  // Duration in seconds
   239  func (w *WaitLiveMsg) Duration() time.Duration {
   240  	w.tickerdMutex.RLock()
   241  	var duration = time.Since(w.start)
   242  	w.tickerdMutex.RUnlock()
   243  	return duration
   244  }
   245  
   246  func (w *WaitLiveMsg) print() {
   247  	var buf = bytes.Buffer{}
   248  
   249  	w.msgsMutex.RLock()
   250  	var msgs = w.msgs
   251  	w.msgsMutex.RUnlock()
   252  
   253  	for _, m := range msgs {
   254  		var txt = m.getText()
   255  
   256  		if len(txt) == 0 {
   257  			continue
   258  		}
   259  
   260  		var s = m.getSymbol()
   261  
   262  		if len(s) != 0 {
   263  			buf.WriteString(s)
   264  			buf.WriteString(" ")
   265  		}
   266  
   267  		buf.WriteString(txt)
   268  		buf.WriteString("\n")
   269  	}
   270  
   271  	w.pb(buf)
   272  }
   273  
   274  func (w *WaitLiveMsg) pb(buf bytes.Buffer) {
   275  	w.msgsMutex.Lock()
   276  	defer w.msgsMutex.Unlock()
   277  	_, _ = fmt.Fprintf(w.stream, "%v", buf.String())
   278  	_ = w.stream.Flush()
   279  }