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 }