github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/cmd/state/internal/cmdtree/exechandlers/messenger/messenger.go (about)

     1  package messenger
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/ActiveState/cli/internal/captain"
     8  	"github.com/ActiveState/cli/internal/errs"
     9  	"github.com/ActiveState/cli/internal/graph"
    10  	"github.com/ActiveState/cli/internal/locale"
    11  	"github.com/ActiveState/cli/internal/logging"
    12  	"github.com/ActiveState/cli/internal/multilog"
    13  	"github.com/ActiveState/cli/internal/output"
    14  	"github.com/ActiveState/cli/internal/rtutils/ptr"
    15  	"github.com/ActiveState/cli/pkg/platform/model"
    16  	"golang.org/x/net/context"
    17  )
    18  
    19  type Messenger struct {
    20  	out      output.Outputer
    21  	svcModel *model.SvcModel
    22  	messages []*graph.MessageInfo
    23  }
    24  
    25  func New(out output.Outputer, svcModel *model.SvcModel) *Messenger {
    26  	return &Messenger{
    27  		out:      out,
    28  		svcModel: svcModel,
    29  	}
    30  }
    31  
    32  func (m *Messenger) OnExecStart(cmd *captain.Command, _ []string) error {
    33  	if m.out.Type().IsStructured() {
    34  		// No point showing messaging on non-plain output (eg. json)
    35  		return nil
    36  	}
    37  
    38  	if cmd.Name() == "update" {
    39  		return nil // do not print update/deprecation warnings/messages when running `state update`
    40  	}
    41  
    42  	cmds := cmd.JoinedCommandNames()
    43  	flags := cmd.ActiveFlagNames()
    44  
    45  	messages, err := m.svcModel.CheckMessages(context.Background(), cmds, flags)
    46  	if err != nil {
    47  		multilog.Error("Could not report messages as CheckMessages return an error: %s", errs.JoinMessage(err))
    48  	}
    49  
    50  	m.messages = messages
    51  
    52  	logging.Debug("Received %d messages to print", len(messages))
    53  
    54  	if err := m.PrintByPlacement(graph.MessagePlacementTypeBeforeCmd); err != nil {
    55  		return errs.Wrap(err, "message error occurred before cmd")
    56  	}
    57  
    58  	return nil
    59  }
    60  
    61  func (m *Messenger) OnExecStop(cmd *captain.Command, _ []string) error {
    62  	if m.out.Type().IsStructured() {
    63  		// No point showing messaging on non-plain output (eg. json)
    64  		return nil
    65  	}
    66  
    67  	if cmd.Name() == "update" {
    68  		return nil // do not print update/deprecation warnings/messages when running `state update`
    69  	}
    70  
    71  	if err := m.PrintByPlacement(graph.MessagePlacementTypeAfterCmd); err != nil {
    72  		return errs.Wrap(err, "message error occurred before cmd")
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  func (m *Messenger) PrintByPlacement(placement graph.MessagePlacementType) error {
    79  	exit := []string{}
    80  
    81  	messages := []*graph.MessageInfo{}
    82  	for _, message := range m.messages {
    83  		if message.Placement != placement {
    84  			logging.Debug("Skipping message %s as it's placement (%s) does not match %s", message.ID, message.Placement, placement)
    85  			messages = append(messages, message)
    86  			continue
    87  		}
    88  
    89  		if placement == graph.MessagePlacementTypeAfterCmd {
    90  			m.out.Notice("") // Line break after
    91  		}
    92  
    93  		logging.Debug("Printing message: %s", message.ID)
    94  		m.out.Notice(message.Message)
    95  
    96  		if placement == graph.MessagePlacementTypeBeforeCmd {
    97  			m.out.Notice("") // Line break before
    98  		}
    99  
   100  		if message.Interrupt == graph.MessageInterruptTypePrompt {
   101  			if m.out.Config().Interactive {
   102  				m.out.Print(locale.Tl("messenger_prompt_continue", "Press ENTER to continue."))
   103  				fmt.Scanln(ptr.To("")) // Wait for input from user
   104  			} else {
   105  				logging.Debug("Skipping message prompt as we're not in interactive mode")
   106  			}
   107  		}
   108  
   109  		if message.Interrupt == graph.MessageInterruptTypeExit {
   110  			exit = append(exit, message.ID)
   111  		}
   112  	}
   113  
   114  	m.messages = messages
   115  
   116  	if len(exit) > 0 {
   117  		// It's the responsibility of the message to give the user context as to why this exit happened.
   118  		// We pass an input error here to ensure this doesn't get logged.
   119  		return errs.Silence(errs.WrapExitCode(errs.New("Following messages triggered exit: %s", strings.Join(exit, ", ")), 1))
   120  	}
   121  
   122  	return nil
   123  }