github.com/swaros/contxt/module/runner@v0.0.0-20240305083542-3dbd4436ac40/execouthndl.go (about)

     1  // MIT License
     2  //
     3  // Copyright (c) 2020 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved.
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the Software), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  
    23  // AINC-NOTE-0815
    24  
    25  package runner
    26  
    27  import (
    28  	"fmt"
    29  	"strings"
    30  	"sync"
    31  
    32  	"github.com/swaros/contxt/module/ctxout"
    33  	"github.com/swaros/contxt/module/systools"
    34  	"github.com/swaros/contxt/module/tasks"
    35  )
    36  
    37  var (
    38  	// random color helper
    39  	randColors = NewRandColorStore()
    40  	// color for the state label
    41  	stateColorPreDef = ctxout.ToString(ctxout.NewMOWrap(), ctxout.ForeLightCyan+ctxout.BoldTag+ctxout.BackBlue)
    42  	processPreDef    = ctxout.ToString(ctxout.NewMOWrap(), ctxout.ForeLightCyan+ctxout.BackBlack)
    43  	pidPreDef        = ctxout.ToString(ctxout.NewMOWrap(), ctxout.ForeLightYellow+ctxout.BackBlack)
    44  	commentPreDef    = ctxout.ToString(ctxout.NewMOWrap(), ctxout.ForeLightBlue+ctxout.BackBlack)
    45  )
    46  
    47  // short for drawRowWithLabels("", "", label, labelColor, content, contentColor, info, infoColor)
    48  func (c *CmdExecutorImpl) drawRow(label, labelColor, content, contentColor, info, infoColor string) {
    49  	c.drawRowWithLabels("", "", label, labelColor, content, contentColor, info, infoColor)
    50  }
    51  
    52  // draws a row with labels and colors
    53  // leftLabel and rightLabel are optional
    54  // labelColor is the color markup for the label
    55  // contentColor is the color markup for the content
    56  // infoColor is the color markup for the info
    57  // label, content and info are the strings to print
    58  func (c *CmdExecutorImpl) drawRowWithLabels(leftLabel, rightLabel, label, labelColor, content, contentColor, info, infoColor string) {
    59  	if leftLabel == "" {
    60  		leftLabel = "<sign runident> "
    61  	}
    62  	if rightLabel == "" {
    63  		rightLabel = "<sign stopident> "
    64  	}
    65  	leftLabel = ctxout.ToString(ctxout.NewMOWrap(), ctxout.ForeYellow+leftLabel+labelColor)
    66  	rightLabel = ctxout.ToString(ctxout.NewMOWrap(), ctxout.ForeYellow+rightLabel+contentColor)
    67  	c.Println(
    68  		ctxout.Row(
    69  
    70  			ctxout.TD(
    71  				label,
    72  				ctxout.Prop(ctxout.AttrSize, 10),
    73  				ctxout.Prop(ctxout.AttrOrigin, 2),
    74  				ctxout.Prop(ctxout.AttrPrefix, leftLabel),
    75  				ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
    76  				//ctxout.Margin(4), // 4 spaces (run + space * 2 )
    77  			),
    78  
    79  			ctxout.TD(
    80  				content,
    81  				ctxout.Prop(ctxout.AttrSize, 85),
    82  				ctxout.Prop(ctxout.AttrPrefix, rightLabel),
    83  				ctxout.Prop(ctxout.AttrOverflow, "wordwrap"),
    84  				ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
    85  			),
    86  			ctxout.TD(
    87  				info,
    88  				ctxout.Prop(ctxout.AttrSize, 5),
    89  				ctxout.Prop(ctxout.AttrOrigin, 2),
    90  				ctxout.Prop(ctxout.AttrPrefix, infoColor),
    91  				ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
    92  				ctxout.Margin(1), // 1 space for being sure
    93  			),
    94  		),
    95  	)
    96  }
    97  
    98  // handles all the incomming messages from the tasks
    99  // depending on the message type it will print the message.
   100  // for this we parsing at the fist level the message type for each message.
   101  
   102  func (c *CmdExecutorImpl) getOutHandler() func(msg ...interface{}) {
   103  	return func(msg ...interface{}) {
   104  		var m sync.Mutex
   105  		m.Lock()
   106  		// go through all messages
   107  		for _, m := range msg {
   108  			// hanlde the message type
   109  			switch tm := m.(type) {
   110  
   111  			case tasks.MsgPid:
   112  				targetColor, ok := randColors.GetOrSetRandomColor(tm.Target)
   113  				if !ok {
   114  					targetColor = RandColor{foreColor: "white", backColor: "black"}
   115  				}
   116  				c.Println(
   117  					ctxout.Row(
   118  
   119  						ctxout.TD(
   120  							"PROCESS PID "+ctxout.BaseSignScreen+" ",
   121  							ctxout.Prop(ctxout.AttrSize, 10),
   122  							ctxout.Right(),
   123  							ctxout.Prop(ctxout.AttrPrefix, processPreDef),
   124  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   125  						),
   126  
   127  						ctxout.TD(
   128  							tm.Pid,
   129  							ctxout.Right(),
   130  							ctxout.Prop(ctxout.AttrSize, 5),
   131  							ctxout.Prop(ctxout.AttrPrefix, pidPreDef),
   132  							ctxout.Prop(ctxout.AttrOverflow, "ignore"),
   133  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   134  						),
   135  						ctxout.TD(
   136  							" .... ",
   137  							ctxout.Prop(ctxout.AttrSize, 70),
   138  							ctxout.Prop(ctxout.AttrPrefix, commentPreDef),
   139  							ctxout.Prop(ctxout.AttrOverflow, "ignore"),
   140  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   141  						),
   142  						ctxout.TD(
   143  							" "+tm.Target,
   144  							ctxout.Prop(ctxout.AttrSize, 14),
   145  							ctxout.Prop(ctxout.AttrPrefix, targetColor.ColorMarkup()),
   146  							ctxout.Prop(ctxout.AttrOverflow, "ignore"),
   147  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   148  						),
   149  					),
   150  				)
   151  
   152  			case tasks.MsgCommand:
   153  				c.drawRow(
   154  					"executed command",
   155  					ctxout.ForeYellow+ctxout.BoldTag,
   156  					systools.AnyToStrNoTabs(tm),
   157  					ctxout.ForeDarkGrey,
   158  					ctxout.BaseSignScreen+" ",
   159  					ctxout.ForeYellow,
   160  				)
   161  
   162  			// this is a special case where we need to
   163  			// check against the context of the message
   164  			case tasks.MsgTarget:
   165  				// we get the command for the target
   166  				// because we handle a target message,
   167  				// we look for the target color
   168  				targetColor, ok := randColors.GetOrSetRandomColor(tm.Target)
   169  				if !ok {
   170  					targetColor = RandColor{foreColor: "white", backColor: "black"}
   171  				}
   172  				switch tm.Context {
   173  
   174  				case "command":
   175  					c.drawRow(
   176  						tm.Target,
   177  						targetColor.ColorMarkup(),
   178  						"cmd: "+tm.Info,
   179  						ctxout.ForeDarkGrey,
   180  						ctxout.BaseSignScreen+" ",
   181  						ctxout.ForeYellow,
   182  					)
   183  
   184  				case "needs_required":
   185  					c.drawRow(
   186  						tm.Target,
   187  						targetColor.ColorMarkup(),
   188  						"require: "+strings.Join(strings.Split(tm.Info, ","), " "),
   189  						ctxout.ForeDarkGrey,
   190  						ctxout.BaseSignDebug,
   191  						ctxout.ForeBlue,
   192  					)
   193  
   194  				case "needs_execute":
   195  					c.drawRow(
   196  						tm.Target,
   197  						targetColor.ColorMarkup(),
   198  						"execute: "+tm.Info,
   199  						ctxout.ForeDarkGrey+ctxout.BoldTag+ctxout.Dim,
   200  						ctxout.BaseSignScreen+" ",
   201  						ctxout.ForeMagenta,
   202  					)
   203  
   204  				case "needs_done":
   205  					c.drawRow(
   206  						tm.Target,
   207  						ctxout.ForeLightCyan,
   208  						tm.Info,
   209  						ctxout.ForeDarkGrey,
   210  						ctxout.BaseSignSuccess+ctxout.BaseSignSuccess+ctxout.BaseSignSuccess+" ", // three green success signs for all subneeds done
   211  						ctxout.ForeGreen,
   212  					)
   213  
   214  				case "wait_next_done":
   215  					c.drawRow(
   216  						ctxout.BaseSignSuccess+" "+tm.Target,
   217  						ctxout.ForeGreen,
   218  						"DONE ..."+tm.Info,
   219  						ctxout.ForeDarkGrey,
   220  						ctxout.BaseSignSuccess+" ",
   221  						ctxout.ForeBlue,
   222  					)
   223  
   224  				default:
   225  					c.Println(
   226  						ctxout.ForeCyan,
   227  						" [",
   228  						ctxout.ForeLightYellow,
   229  						tm.Target,
   230  						ctxout.ForeCyan,
   231  						"]",
   232  						ctxout.ForeLightCyan,
   233  						tm.Info,
   234  						ctxout.ForeCyan,
   235  						tm.Context,
   236  						ctxout.CleanTag,
   237  					)
   238  				}
   239  				// end of switch tm.Context
   240  			case tasks.MsgReason, tasks.MsgType:
   241  				msg := fmt.Sprintf("%v", tm)
   242  				if msg == "target-async-group-created" {
   243  					c.drawRow(
   244  						"system",
   245  						ctxout.ForeLightBlue,
   246  						"running async group ...",
   247  						ctxout.ForeBlue,
   248  						ctxout.BaseSignInfo+" ", // three green success signs for all subneeds done
   249  						ctxout.ForeYellow,
   250  					)
   251  				} else {
   252  					c.drawRow(
   253  						"info",
   254  						ctxout.ForeLightCyan,
   255  						fmt.Sprintf("%v", tm),
   256  						ctxout.ForeDarkGrey,
   257  						ctxout.BaseSignInfo+" ", // three green success signs for all subneeds done
   258  						ctxout.ForeBlue,
   259  					)
   260  				}
   261  			case *tasks.MsgInfo:
   262  				c.Println(
   263  					ctxout.ForeLightMagenta,
   264  					"[info]",
   265  					ctxout.ForeMagenta,
   266  					tm,
   267  					ctxout.CleanTag,
   268  				)
   269  			// something depending the process is changed.
   270  			// could be one of these:
   271  			// - started
   272  			// - stopped
   273  			// - aborted
   274  			// the comment contains more details.
   275  			// on started it contains the command that is executed
   276  			// on stopped it contains the exit codes (first the real code from the process, second the code from the command)
   277  			// on aborted it contains the reason why the process is aborted. this is a controlled abort. nothing from system side.
   278  			//            this depends on a defined stopreason, so contxt itself is aborting the process.
   279  			case tasks.MsgProcess:
   280  				targetColor, ok := randColors.GetOrSetRandomColor(tm.Target)
   281  				if !ok {
   282  					targetColor = RandColor{foreColor: "white", backColor: "black"}
   283  				}
   284  				c.Println(
   285  					ctxout.Row(
   286  
   287  						ctxout.TD(
   288  							"PROCESS "+ctxout.BaseSignScreen+" ",
   289  							ctxout.Prop(ctxout.AttrSize, 10),
   290  							ctxout.Right(),
   291  							ctxout.Prop(ctxout.AttrPrefix, processPreDef),
   292  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   293  						),
   294  
   295  						ctxout.TD(
   296  							" "+tm.StatusChange+" ",
   297  							ctxout.Prop(ctxout.AttrSize, 5),
   298  							ctxout.Right(),
   299  							ctxout.Prop(ctxout.AttrPrefix, stateColorPreDef),
   300  							ctxout.Prop(ctxout.AttrOverflow, "ignore"),
   301  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   302  						),
   303  						ctxout.TD(
   304  							" "+tm.Comment,
   305  							ctxout.Prop(ctxout.AttrSize, 70),
   306  							ctxout.Prop(ctxout.AttrPrefix, commentPreDef),
   307  							ctxout.Prop(ctxout.AttrOverflow, "ignore"),
   308  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   309  						),
   310  
   311  						ctxout.TD(
   312  							" "+tm.Target,
   313  							ctxout.Prop(ctxout.AttrSize, 14),
   314  							ctxout.Prop(ctxout.AttrPrefix, targetColor.ColorMarkup()),
   315  							ctxout.Prop(ctxout.AttrOverflow, "ignore"),
   316  							ctxout.Prop(ctxout.AttrSuffix, ctxout.CleanTag),
   317  						),
   318  					),
   319  				)
   320  			case tasks.MsgError:
   321  				c.drawRow(
   322  					tm.Target,
   323  					ctxout.ForeLightYellow+ctxout.BoldTag+ctxout.BackRed,
   324  					tm.Err.Error(),
   325  					ctxout.ForeLightRed,
   326  					" "+ctxout.BaseSignError+" ",
   327  					//"error",
   328  					ctxout.ForeYellow+ctxout.BoldTag+ctxout.BackRed,
   329  				)
   330  
   331  			case tasks.MsgInfo:
   332  				c.Println(
   333  					ctxout.ForeYellow,
   334  					"INFO",
   335  					ctxout.ForeLightYellow,
   336  					tm,
   337  					ctxout.CleanTag,
   338  				)
   339  			case tasks.MsgExecOutput:
   340  				// getting forground, background and the sign color for the arrow char
   341  				fg, bg, sc := randColors.GetColorAsCtxMarkup(tm.Target)
   342  				c.drawRowWithLabels(
   343  					" ",
   344  					sc+"<sign runident> ",
   345  					tm.Target,
   346  					fg+bg, //ctxout.ForeWhite+ctxout.BackBlue,
   347  					systools.AnyToStrNoTabs(tm.Output),
   348  					ctxout.ResetCode,
   349  					ctxout.BaseSignScreen+" ",
   350  					ctxout.ForeLightBlue,
   351  				)
   352  
   353  			default:
   354  				// uncomment for dispaying the type of the message that is not handled yet
   355  
   356  				/*
   357  					c.Println(
   358  						fmt.Sprintf("%T", tm),
   359  						ctxout.ForeLightYellow,
   360  						fmt.Sprintf("%v", msg),
   361  						ctxout.ForeWhite,
   362  						ctxout.BackBlack,
   363  						"not implemented yet",
   364  						ctxout.CleanTag,
   365  					)
   366  				*/
   367  
   368  			}
   369  		}
   370  
   371  		m.Unlock()
   372  	}
   373  }