github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekalog/encoder_console_private.go (about)

     1  // Copyright © 2020-2021. All rights reserved.
     2  // Author: Ilya Stroy.
     3  // Contacts: iyuryevich@pm.me, https://github.com/qioalice
     4  // License: https://opensource.org/licenses/MIT
     5  
     6  package ekalog
     7  
     8  import (
     9  	"bytes"
    10  	"io"
    11  	"math"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/qioalice/ekago/v3/ekamath"
    17  	"github.com/qioalice/ekago/v3/ekasys"
    18  	"github.com/qioalice/ekago/v3/internal/ekaletter"
    19  
    20  	"github.com/json-iterator/go"
    21  )
    22  
    23  // noinspection GoSnakeCaseUsage
    24  type (
    25  	// _CICE_FormatPart represents built format's part (parsed RAW format string).
    26  	// - for 'typ' == '_CICE_FPT_VERB_JUST_TEXT', 'value' is original RAW format string's part;
    27  	// - for 'typ' == '_CICE_FPT_VERB_COLOR_CUSTOM', 'value' is calculated bash escape sequence
    28  	//   (like "\033[01;03;38;05;144m");
    29  	// - for other 'typ' variants, 'value' is empty, 'cause it's log's verbs
    30  	// and they're runtime calculated entities.
    31  	_CICE_FormatPart struct {
    32  		typ   _CICE_FormatPartType
    33  		value string
    34  	}
    35  
    36  	// _CICE_FormatPartType is a special type of _CICE_FormatPart's field 'typ' that contains
    37  	// an info what kind of format verb current _CICE_FormatPart object is
    38  	// and how exactly it will be converted to the text at the runtime.
    39  	_CICE_FormatPartType uint16
    40  
    41  	_CICE_FieldsFormat struct {
    42  		isSet                bool
    43  		beforeFields         string
    44  		afterFields          string
    45  		beforeKey            string
    46  		afterKey             string
    47  		afterValue           string
    48  		afterNewLine         string
    49  		afterNewLineForError string
    50  		itemsPerLine         int16
    51  	}
    52  
    53  	_CICE_BodyFormat struct {
    54  		isSet      bool
    55  		beforeBody string
    56  		afterBody  string
    57  	}
    58  
    59  	_CICE_CallerFormat struct {
    60  		isSet     bool
    61  		isDefault bool
    62  		parts     [10]struct {
    63  			typ int16
    64  			val string
    65  		}
    66  	}
    67  
    68  	_CICE_StacktraceFormat struct {
    69  		isSet       bool
    70  		beforeStack string
    71  		afterStack  string
    72  	}
    73  
    74  	_CICE_ErrorFormat struct {
    75  		isSet       bool
    76  		beforeError string
    77  		afterError  string
    78  	}
    79  
    80  	_CICE_DropColors struct {
    81  		buf  bytes.Buffer
    82  		dest io.Writer
    83  	}
    84  )
    85  
    86  // noinspection GoSnakeCaseUsage
    87  const (
    88  	// Common Integrator Console Encoder Format Part Type (CICE FPT)
    89  	// predefined constants.
    90  
    91  	_CICE_FPT_MASK_TYPE _CICE_FormatPartType = 0x00_FF
    92  	_CICE_FPT_MASK_DATA _CICE_FormatPartType = 0xFF_00
    93  
    94  	_CICE_FPT_VERB_JUST_TEXT       _CICE_FormatPartType = 0x01
    95  	_CICE_FPT_VERB_COLOR_CUSTOM    _CICE_FormatPartType = 0x02
    96  	_CICE_FPT_VERB_COLOR_FOR_LEVEL _CICE_FormatPartType = 0x03
    97  	_CICE_FPT_VERB_BODY            _CICE_FormatPartType = 0x0A
    98  	_CICE_FPT_VERB_TIME            _CICE_FormatPartType = 0x0B
    99  	_CICE_FPT_VERB_LEVEL           _CICE_FormatPartType = 0x0C
   100  	_CICE_FPT_VERB_STACKTRACE      _CICE_FormatPartType = 0x1A
   101  	_CICE_FPT_VERB_FIELDS          _CICE_FormatPartType = 0x2A
   102  	_CICE_FPT_VERB_CALLER          _CICE_FormatPartType = 0x3A
   103  
   104  	// Common Integrator Console Encoder Level Format (CICE LF)
   105  	// type constants.
   106  
   107  	_CICE_LF_NUMBER           _CICE_FormatPartType = 1
   108  	_CICE_LF_SHORT_NORMAL     _CICE_FormatPartType = 2
   109  	_CICE_LF_SHORT_UPPER_CASE _CICE_FormatPartType = 3
   110  	_CICE_LF_FULL_NORMAL      _CICE_FormatPartType = 4
   111  	_CICE_LF_FULL_UPPER_CASE  _CICE_FormatPartType = 5
   112  
   113  	// Common Integrator Console Encoder Time Format (CICE TF)
   114  	// type constants.
   115  
   116  	_CICE_TF_TIMESTAMP _CICE_FormatPartType = 1
   117  	_CICE_TF_ANSIC     _CICE_FormatPartType = 2
   118  	_CICE_TF_UNIXDATE  _CICE_FormatPartType = 3
   119  	_CICE_TF_RUBYDATE  _CICE_FormatPartType = 4
   120  	_CICE_TF_RFC822    _CICE_FormatPartType = 5
   121  	_CICE_TF_RFC822_Z  _CICE_FormatPartType = 6
   122  	_CICE_TF_RFC850    _CICE_FormatPartType = 7
   123  	_CICE_TF_RFC1123   _CICE_FormatPartType = 8
   124  	_CICE_TF_RFC1123_Z _CICE_FormatPartType = 9
   125  	_CICE_TF_RFC3339   _CICE_FormatPartType = 10
   126  
   127  	// Common Integrator Console Encoder Caller Format (CICE CF)
   128  	// type constants.
   129  
   130  	_CICE_CF_TYPE_SEPARATOR  int16 = -1
   131  	_CICE_CF_TYPE_FUNC_SHORT int16 = 1
   132  	_CICE_CF_TYPE_FUNC_FULL  int16 = 2
   133  	_CICE_CF_TYPE_FILE_SHORT int16 = 3
   134  	_CICE_CF_TYPE_FILE_FULL  int16 = 4
   135  	_CICE_CF_TYPE_LINE_NUM   int16 = 5
   136  	_CICE_CF_TYPE_PKG_SHORT  int16 = 6 // unused
   137  	_CICE_CF_TYPE_PKG_FULL   int16 = 7
   138  
   139  	// Common Integrator Console Encoder (CICE) verb predefined constants.
   140  
   141  	_CICE_VERB_START_INDICATOR rune = '{'
   142  	_CICE_VERB_END_INDICATOR   rune = '}'
   143  	_CICE_VERB_SEPARATOR       byte = '/'
   144  
   145  	// Common Integrator Console Integrator Standard Colors (CICE SC)
   146  	// predefined constants.
   147  
   148  	_CICE_SC_DEBUG     string = "c/fg:#b2b2b2"
   149  	_CICE_SC_INFO      string = "c/fg:#87d7ff"
   150  	_CICE_SC_NOTICE    string = "c/fg:#00d700"
   151  	_CICE_SC_WARNING   string = "c/fg:#ff8700"
   152  	_CICE_SC_ERROR     string = "c/fg:#ff0000"
   153  	_CICE_SC_CRITICAL  string = "c/fg:#ffaf00/b"
   154  	_CICE_SC_ALERT     string = "c/fg:#ffaf00/b/u"
   155  	_CICE_SC_EMERGENCY string = "c/fg:#ff00ff/b/u"
   156  
   157  	// Common Integrator Console Encoder (CICE) defaults.
   158  
   159  	_CICE_DEFAULT_FORMAT string = "{{c}}[{{l/SS}}] <{{t}}>:{{c/0}} " + // include colored level, colored time
   160  		"{{m/?$\n}}" + // include message with \n if non-empty
   161  		"{{f/?$\n/v = /e, /l\t/le\t\t}}" + // include fields with " = " as key-value separator
   162  		"{{s/?$\n/e, }}" + // include stacktrace with \n if non-empty
   163  		"{{w/0/fd}}" + // omit caller, specify each stacktrace's frame format
   164  		"\n"
   165  
   166  	_CICE_DEFAULT_TIME_FORMAT string = "Mon Jan 02 15:04:05"
   167  )
   168  
   169  // CommonIntegrator CI_ConsoleEncoder Verb Types (CICE VT)
   170  // that are used in format string to determine what kind of verb must be used.
   171  var (
   172  	cevtCaller     = []string{"caller", "who", "w"}
   173  	cevtColor      = []string{"color", "c"}
   174  	cevtLevel      = []string{"level", "lvl", "l"}
   175  	cevtTime       = []string{"time", "t"}
   176  	cevtMessage    = []string{"message", "body", "m", "b"}
   177  	cevtFields     = []string{"fields", "f"}
   178  	cevtStacktrace = []string{"stacktrace", "s"}
   179  )
   180  
   181  var (
   182  	// Make sure we won't break API by declaring package's console encoder.
   183  	defaultConsoleEncoder CI_Encoder
   184  )
   185  
   186  // Type extracts verb's type from current _CICE_FormatPartType that can be compared
   187  // with _CEFPT_VERB... constants.
   188  func (fpt _CICE_FormatPartType) Type() _CICE_FormatPartType {
   189  	return fpt & _CICE_FPT_MASK_TYPE
   190  }
   191  
   192  // Data extracts verb's type data from current _CICE_FormatPartType that is needed
   193  // for some internal verb's encoders.
   194  func (fpt _CICE_FormatPartType) Data() _CICE_FormatPartType {
   195  	return (fpt & _CICE_FPT_MASK_DATA) >> 8
   196  }
   197  
   198  // doBuild builds the current CI_ConsoleEncoder only if it has not built yet.
   199  // There is no-op if format string is empty or encoder already built.
   200  func (ce *CI_ConsoleEncoder) doBuild() *CI_ConsoleEncoder {
   201  
   202  	switch {
   203  	case ce == nil:
   204  		return nil
   205  
   206  	case len(ce.formatParts) > 0:
   207  		// do not build if it's so already
   208  		return ce
   209  	}
   210  
   211  	ce.format = strings.TrimSpace(ce.format)
   212  	if ce.format == "" {
   213  		ce.format = _CICE_DEFAULT_FORMAT
   214  	}
   215  
   216  	// start parsing ce.format
   217  	// all parsing loops are for-range based (because there is UTF-8 support)
   218  	// (yes, you can use not only ASCII parts in your format string,
   219  	// and yes if you do it, you are mad. stop it!).
   220  	for rest := ce.format; rest != ""; rest = ce.parseFirstVerb(rest) {
   221  	}
   222  
   223  	ce.uniteJustTextVerbs()
   224  	ce.setStandardParts()
   225  
   226  	return ce
   227  }
   228  
   229  // uniteJustTextVerbs unites "just text" verbs in 'ce.formatParts'
   230  // that follows each other. It may happen when something with bad verbs
   231  // were included in 'ce.format'.
   232  func (ce *CI_ConsoleEncoder) uniteJustTextVerbs() *CI_ConsoleEncoder {
   233  
   234  	var (
   235  		// idx of "just text" verb next "just text" verbs will be united with
   236  		justTextVerbIdx = -1
   237  		// new out slice of verbs
   238  		newFormatParts = make([]_CICE_FormatPart, 0, len(ce.formatParts))
   239  	)
   240  
   241  	for idx, verb := range ce.formatParts {
   242  		switch verbTyp := verb.typ.Type(); {
   243  
   244  		case justTextVerbIdx == -1 && verbTyp == _CICE_FPT_VERB_JUST_TEXT:
   245  			justTextVerbIdx = idx
   246  
   247  		case justTextVerbIdx != -1 && verbTyp == _CICE_FPT_VERB_JUST_TEXT:
   248  			ce.formatParts[justTextVerbIdx].value += verb.value
   249  
   250  		case justTextVerbIdx != -1 && verbTyp != _CICE_FPT_VERB_JUST_TEXT:
   251  			newFormatParts = append(newFormatParts, ce.formatParts[justTextVerbIdx])
   252  			justTextVerbIdx = -1
   253  			fallthrough
   254  
   255  		case justTextVerbIdx == -1 && verbTyp != _CICE_FPT_VERB_JUST_TEXT:
   256  			newFormatParts = append(newFormatParts, verb)
   257  		}
   258  	}
   259  
   260  	// it was definitely the last "just text" verb we should add
   261  	if justTextVerbIdx != -1 {
   262  		newFormatParts = append(newFormatParts, ce.formatParts[justTextVerbIdx])
   263  	}
   264  
   265  	ce.formatParts = newFormatParts[:]
   266  	return ce
   267  }
   268  
   269  // setStandardParts saves standard colors for standard log levels
   270  // if they has not been set yet.
   271  func (ce *CI_ConsoleEncoder) setStandardParts() *CI_ConsoleEncoder {
   272  
   273  	if ce.colorMap == nil {
   274  		ce.colorMap = make(map[Level]string)
   275  	}
   276  
   277  	if ce.colorMap[LEVEL_DEBUG] == "" {
   278  		ce.colorMap[LEVEL_DEBUG] = ce.rvColorHelper(_CICE_SC_DEBUG)
   279  	}
   280  	if ce.colorMap[LEVEL_INFO] == "" {
   281  		ce.colorMap[LEVEL_INFO] = ce.rvColorHelper(_CICE_SC_INFO)
   282  	}
   283  	if ce.colorMap[LEVEL_NOTICE] == "" {
   284  		ce.colorMap[LEVEL_NOTICE] = ce.rvColorHelper(_CICE_SC_NOTICE)
   285  	}
   286  	if ce.colorMap[LEVEL_WARNING] == "" {
   287  		ce.colorMap[LEVEL_WARNING] = ce.rvColorHelper(_CICE_SC_WARNING)
   288  	}
   289  	if ce.colorMap[LEVEL_ERROR] == "" {
   290  		ce.colorMap[LEVEL_ERROR] = ce.rvColorHelper(_CICE_SC_ERROR)
   291  	}
   292  	if ce.colorMap[LEVEL_CRITICAL] == "" {
   293  		ce.colorMap[LEVEL_CRITICAL] = ce.rvColorHelper(_CICE_SC_CRITICAL)
   294  	}
   295  	if ce.colorMap[LEVEL_ALERT] == "" {
   296  		ce.colorMap[LEVEL_ALERT] = ce.rvColorHelper(_CICE_SC_ALERT)
   297  	}
   298  	if ce.colorMap[LEVEL_EMERGENCY] == "" {
   299  		ce.colorMap[LEVEL_EMERGENCY] = ce.rvColorHelper(_CICE_SC_EMERGENCY)
   300  	}
   301  
   302  	if !ce.cf.isSet {
   303  		ce.cf.isDefault = true
   304  	}
   305  
   306  	return ce
   307  }
   308  
   309  // parseFirstVerb parses 'format', extracts first verb (even if it's "just text"
   310  // verb), saves it to ce.formatParts and then returns the rest of 'format' string.
   311  func (ce *CI_ConsoleEncoder) parseFirstVerb(format string) (rest string) {
   312  
   313  	var (
   314  		i   = 0
   315  		pc  rune // prev char
   316  		wv  bool // true if current parsing verb is complex verb (not "just text")
   317  		wve bool // true if complex verb has been closed correctly
   318  	)
   319  
   320  	// loop is for-range based (because there is UTF-8 support)
   321  	// (yes, you can use not only ASCII parts in your format string,
   322  	// and yes if you do it, you are mad. stop it!).
   323  	for _, c := range format {
   324  		switch {
   325  		case c == _CICE_VERB_START_INDICATOR && pc == _CICE_VERB_START_INDICATOR && wv:
   326  			// unexpected "{{" inside complex verb, treat all prev as "just text",
   327  			// try to treat as starting complex verb
   328  			wv = false
   329  			i--
   330  
   331  		case c == _CICE_VERB_START_INDICATOR && pc == _CICE_VERB_START_INDICATOR && i > 1:
   332  			// > 1 (not > 0) because if string started with "{{", after first "{"
   333  			// i already == 1.
   334  			//
   335  			// was "just text", found complex verb start
   336  			i--
   337  
   338  		case c == _CICE_VERB_END_INDICATOR && pc == _CICE_VERB_END_INDICATOR && wv:
   339  			// ending of complex verb
   340  			wve = true
   341  			i++
   342  
   343  		case c == _CICE_VERB_START_INDICATOR && pc == _CICE_VERB_START_INDICATOR:
   344  			// this is the beginning of 'format' and of complex verb
   345  			wv = true
   346  			i++
   347  			continue
   348  
   349  		default:
   350  			pc = c
   351  			i++
   352  			continue
   353  		}
   354  		break
   355  	}
   356  
   357  	// what kind of verb did we parse and whether verb has been closed correctly?
   358  	if wv && wve {
   359  		ce.minimumBufferLen += ce.rv(format[:i])
   360  	} else {
   361  		ce.minimumBufferLen += ce.rvJustText(format[:i])
   362  	}
   363  
   364  	return format[i:]
   365  }
   366  
   367  // rv (resolve verb) tries to determine what kind of complex verb 'verb' is,
   368  // creates related '_CICE_FormatPart', fills it and adds to 'ce.formatParts'.
   369  // Returned predicted minimum length of bytes that required in buffer to store
   370  // formatted verb.
   371  func (ce *CI_ConsoleEncoder) rv(verb string) (predictedLen int) {
   372  
   373  	// applyOnce is a helper func to avoid many if-else statements in tht switch below.
   374  	applyOnce := func(isSet *bool, fallback, applicator func(string) int, verb string) int {
   375  		if !*isSet {
   376  			*isSet = true
   377  			return applicator(verb)
   378  		} else {
   379  			return fallback(verb)
   380  		}
   381  	}
   382  
   383  	// it guarantees that "verb" starts from "{{",
   384  	// so we can remove leading "{{" and trailing "}}"
   385  	switch verb = verb[2 : len(verb)-2]; {
   386  
   387  	case hpm(verb, cevtCaller):
   388  		return applyOnce(&ce.cf.isSet, ce.rvJustText, ce.rvCaller, verb)
   389  
   390  	case hpm(verb, cevtColor):
   391  		return ce.rvColor(verb)
   392  
   393  	case hpm(verb, cevtLevel):
   394  		return ce.rvLevel(verb)
   395  
   396  	case hpm(verb, cevtTime):
   397  		return ce.rvTime(verb)
   398  
   399  	case hpm(verb, cevtMessage):
   400  		return applyOnce(&ce.bf.isSet, ce.rvJustText, ce.rvBody, verb)
   401  
   402  	case hpm(verb, cevtFields):
   403  		return applyOnce(&ce.ff.isSet, ce.rvJustText, ce.rvFields, verb)
   404  
   405  	case hpm(verb, cevtStacktrace):
   406  		return applyOnce(&ce.sf.isSet, ce.rvJustText, ce.rvStacktrace, verb)
   407  
   408  	default:
   409  		// incorrect verb, treat it as "just text" verb
   410  		return ce.rvJustText(verb)
   411  	}
   412  }
   413  
   414  // rvHelper is a part of "resolve verb" functions but moreover it's a helper.
   415  // This function literally have no recognition algorithm but splits 'verb'
   416  // to verb parts (ignoring first part, assuming that its verb's name) and then
   417  // calls 'resolver' for each that part.
   418  // Stops splitting and calling 'resolver' if it's return 'false' one time.
   419  //
   420  // It guarantees that if 'verbPart' is empty, 'resolver' won't be called for that
   421  // and processing will stopped.
   422  //
   423  // Requirements:
   424  // 'verb' != "", 'resolver' != nil. Otherwise no-op.
   425  func (_ *CI_ConsoleEncoder) rvHelper(verb string, resolver func(verbPart string) (continue_ bool)) {
   426  
   427  	if verb == "" || resolver == nil {
   428  		return
   429  	}
   430  
   431  	// skip verb name
   432  	if idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx != -1 {
   433  		verb = verb[idx+1:]
   434  	} else {
   435  		// there is no verb separator, it's simple verb. Do nothing
   436  		return
   437  	}
   438  
   439  	for idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx != -1; {
   440  		if idx == 0 || !resolver(verb[:idx]) {
   441  			goto STOP
   442  		}
   443  		verb = verb[idx+1:]
   444  		idx = strings.IndexByte(verb, _CICE_VERB_SEPARATOR)
   445  	}
   446  
   447  	// parse last entity
   448  	if !resolver(verb) {
   449  		goto STOP
   450  	}
   451  
   452  STOP:
   453  	return
   454  }
   455  
   456  func (ce *CI_ConsoleEncoder) rvJustText(text string) (predictedLen int) {
   457  
   458  	if text != "" {
   459  		ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   460  			typ:   _CICE_FPT_VERB_JUST_TEXT,
   461  			value: text,
   462  		})
   463  	}
   464  
   465  	return len(text)
   466  }
   467  
   468  func (ce *CI_ConsoleEncoder) rvLevel(verb string) (predictedLen int) {
   469  
   470  	formattedLevel := _CICE_LF_FULL_NORMAL
   471  	predictedLen = 9
   472  
   473  	if idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx != -1 {
   474  		switch verb[idx+1:] {
   475  		case "d", "D":
   476  			formattedLevel = _CICE_LF_NUMBER
   477  			predictedLen = 1
   478  		case "s":
   479  			formattedLevel = _CICE_LF_SHORT_NORMAL
   480  			predictedLen = 5
   481  		case "S":
   482  			formattedLevel = _CICE_LF_SHORT_UPPER_CASE
   483  			predictedLen = 5
   484  		case "ss":
   485  			formattedLevel = _CICE_LF_FULL_NORMAL
   486  			predictedLen = 9
   487  		case "SS":
   488  			formattedLevel = _CICE_LF_FULL_UPPER_CASE
   489  			predictedLen = 9
   490  		}
   491  	}
   492  
   493  	ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   494  		typ: _CICE_FPT_VERB_LEVEL | (formattedLevel << 8),
   495  	})
   496  
   497  	return predictedLen
   498  }
   499  
   500  func (ce *CI_ConsoleEncoder) rvTime(verb string) (predictedLen int) {
   501  
   502  	format := _CICE_DEFAULT_TIME_FORMAT
   503  	formattedTime := _CICE_FormatPartType(0)
   504  
   505  	(*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) {
   506  		if verbPart = strings.TrimSpace(format); verbPart != "" {
   507  			switch predefined := strings.ToUpper(verbPart); predefined {
   508  			case "UNIX", "TIMESTAMP":
   509  				formattedTime = _CICE_TF_TIMESTAMP
   510  			case "ANSIC":
   511  				formattedTime = _CICE_TF_ANSIC
   512  			case "UNIXDATE", "UNIX_DATE":
   513  				formattedTime = _CICE_TF_UNIXDATE
   514  			case "RUBYDATE", "RUBY_DATE":
   515  				formattedTime = _CICE_TF_RUBYDATE
   516  			case "RFC822":
   517  				formattedTime = _CICE_TF_RFC822
   518  			case "RFC822Z":
   519  				formattedTime = _CICE_TF_RFC822_Z
   520  			case "RFC850":
   521  				formattedTime = _CICE_TF_RFC850
   522  			case "RFC1123":
   523  				formattedTime = _CICE_TF_RFC1123
   524  			case "RFC1123Z":
   525  				formattedTime = _CICE_TF_RFC1123_Z
   526  			case "RFC3339":
   527  				formattedTime = _CICE_TF_RFC3339
   528  			default:
   529  				format = verbPart
   530  			}
   531  		}
   532  		return false // only first time verb is allowed and will be parsed
   533  	})
   534  
   535  	ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   536  		typ:   _CICE_FPT_VERB_TIME | (formattedTime << 8),
   537  		value: format,
   538  	})
   539  
   540  	return len(time.Now().Format(format)) + 10 // stock for some weekdays
   541  }
   542  
   543  func (ce *CI_ConsoleEncoder) rvColor(verb string) (predictedLen int) {
   544  
   545  	if idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx == -1 {
   546  		ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   547  			typ: _CICE_FPT_VERB_COLOR_FOR_LEVEL,
   548  		})
   549  		return ce.colorMapMax
   550  	}
   551  
   552  	if encodedColor := ce.rvColorHelper(verb); encodedColor != "" {
   553  		ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   554  			typ:   _CICE_FPT_VERB_COLOR_CUSTOM,
   555  			value: encodedColor,
   556  		})
   557  		return len(encodedColor)
   558  	} else {
   559  		return ce.rvJustText(verb)
   560  	}
   561  }
   562  
   563  func (_ *CI_ConsoleEncoder) rvColorHelper(colorVerb string) string {
   564  
   565  	cb := colorBuilder{}
   566  	cb.init()
   567  
   568  	(*CI_ConsoleEncoder)(nil).rvHelper(colorVerb, func(verbPart string) (continue_ bool) {
   569  		return cb.parseEntity(verbPart)
   570  	})
   571  
   572  	return cb.encode()
   573  }
   574  
   575  // rvBody is a part of "resolve verb" functions.
   576  // rvBody tries to parse 'verb' as logger Entry's body and anyway indicates that
   577  // here will be stored Entry's body.
   578  //
   579  // Verb's arguments:
   580  // - For non-empty body:
   581  //   - "?^<text>": <text> will be prepended to the Entry's body at the runtime.
   582  //   - "?$<text>": <text> will be appended to the Entry's body at the runtime.
   583  func (ce *CI_ConsoleEncoder) rvBody(verb string) (predictedLen int) {
   584  
   585  	(*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) {
   586  		switch {
   587  		case strings.HasPrefix(verbPart, "?^"):
   588  			ce.bf.beforeBody = verbPart[2:]
   589  		case strings.HasPrefix(verbPart, "?$"):
   590  			ce.bf.afterBody = verbPart[2:]
   591  		default:
   592  			return false
   593  		}
   594  		return true
   595  	})
   596  
   597  	ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   598  		typ: _CICE_FPT_VERB_BODY,
   599  	})
   600  
   601  	return 256 + len(ce.bf.beforeBody) + len(ce.bf.afterBody)
   602  }
   603  
   604  func (ce *CI_ConsoleEncoder) rvCaller(verb string) (predictedLen int) {
   605  
   606  	isAdd := true
   607  	formatPrefixes := []string{"f", "F"}
   608  
   609  	(*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) {
   610  		switch {
   611  		case verbPart == "0":
   612  			isAdd = false
   613  		case hpm(verbPart, formatPrefixes):
   614  			predictedLen += ce.rvCallerFormat(verbPart[1:])
   615  		default:
   616  			return false
   617  		}
   618  		return true
   619  	})
   620  
   621  	if isAdd {
   622  		ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   623  			typ: _CICE_FPT_VERB_CALLER,
   624  		})
   625  		return predictedLen
   626  	} else {
   627  		return 0
   628  	}
   629  }
   630  
   631  func (ce *CI_ConsoleEncoder) rvCallerFormat(f string) (predictedLen int) {
   632  
   633  	if f == "" || f == "d" || f == "D" {
   634  		ce.cf.isDefault = true
   635  		return 256
   636  	}
   637  
   638  	j := 0 // index of ce.cf.parts
   639  	for _, fc := range f {
   640  
   641  		t := _CICE_CF_TYPE_SEPARATOR // by default threat it as a separator
   642  		switch fc {
   643  
   644  		case 'w':
   645  			t = _CICE_CF_TYPE_FUNC_SHORT
   646  		case 'W':
   647  			t = _CICE_CF_TYPE_FUNC_FULL
   648  		case 'f':
   649  			t = _CICE_CF_TYPE_FILE_SHORT
   650  		case 'F':
   651  			t = _CICE_CF_TYPE_FILE_FULL
   652  		case 'l', 'L':
   653  			t = _CICE_CF_TYPE_LINE_NUM
   654  		case 'p', 'P':
   655  			t = _CICE_CF_TYPE_PKG_FULL
   656  
   657  		default:
   658  			switch ce.cf.parts[j].typ {
   659  			default:
   660  				j++
   661  				fallthrough
   662  			case 0:
   663  				ce.cf.parts[j].typ = _CICE_CF_TYPE_SEPARATOR
   664  				fallthrough
   665  			case -1:
   666  				ce.cf.parts[j].val += string(fc)
   667  			}
   668  			continue
   669  		}
   670  
   671  		// maybe it's already existed? skip it if it so
   672  		for i, n := 0, len(ce.cf.parts); i < n && ce.cf.parts[i].typ != 0; i++ {
   673  			if ce.cf.parts[i].typ == t {
   674  				t = _CICE_CF_TYPE_SEPARATOR
   675  			}
   676  		}
   677  
   678  		if t != _CICE_CF_TYPE_SEPARATOR {
   679  			if ce.cf.parts[j].typ != 0 {
   680  				j++
   681  			}
   682  			ce.cf.parts[j].typ = t
   683  		}
   684  	}
   685  
   686  	return 256
   687  }
   688  
   689  // rvFields is a part of "resolve verb" functions.
   690  // rvFields tries to parse 'verb' as logger Entry's fields (not attached Error's)
   691  // but anyway indicates that here will be stored Entry's body.
   692  //
   693  // Verb's arguments:
   694  // - If at least one LetterField presented:
   695  //   - "?^<text>": <text> will be write before any LetterField is written at the runtime.
   696  //   - "?$<text>": <text> will be appended to the end of last LetterField at the runtime.
   697  //   - "k<text>": <text> will be written before LetterField's keys is written.
   698  //   - "v<text>": <text> will be written before LetterField's value is written.
   699  //   - "e<text>": <text> will be written after LetterField's value excluding last.
   700  //   - "l<text>": <text> will be written at the each new line of fields' part set.
   701  //   - "*<int>": <int> is how much fields are placed at the one line
   702  //     (by default: 4. Use <= 0 value to place all fields at the one line).
   703  func (ce *CI_ConsoleEncoder) rvFields(verb string) (predictedLen int) {
   704  
   705  	ce.ff.itemsPerLine = 4
   706  
   707  	(*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) {
   708  		switch upperCased := strings.ToUpper(verbPart); {
   709  
   710  		case strings.HasPrefix(verbPart, "?^"):
   711  			ce.ff.beforeFields = verbPart[2:]
   712  		case strings.HasPrefix(verbPart, "?$"):
   713  			ce.ff.afterFields = verbPart[2:]
   714  		case strings.HasPrefix(upperCased, "LE"):
   715  			ce.ff.afterNewLineForError = verbPart[2:]
   716  		case upperCased[0] == 'L':
   717  			ce.ff.afterNewLine = verbPart[1:]
   718  		case upperCased[0] == 'K':
   719  			ce.ff.beforeKey = verbPart[1:]
   720  		case upperCased[0] == 'V':
   721  			ce.ff.afterKey = verbPart[1:]
   722  		case upperCased[0] == 'E':
   723  			ce.ff.afterValue = verbPart[1:]
   724  
   725  		case verbPart[0] == '*':
   726  			if perLine_, err := strconv.Atoi(verbPart[1:]); err == nil {
   727  				if perLine_ < 0 {
   728  					ce.ff.itemsPerLine = 0
   729  				} else {
   730  					ce.ff.itemsPerLine = int16(perLine_)
   731  				}
   732  			}
   733  
   734  		default:
   735  			return false
   736  		}
   737  		return true
   738  	})
   739  
   740  	ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   741  		typ: _CICE_FPT_VERB_FIELDS,
   742  	})
   743  
   744  	return 512 + len(ce.ff.beforeFields) + len(ce.ff.afterFields) + len(ce.ff.beforeKey) +
   745  		len(ce.ff.afterKey) + len(ce.ff.afterValue) + len(ce.ff.afterNewLine)
   746  }
   747  
   748  func (ce *CI_ConsoleEncoder) rvStacktrace(verb string) (predictedLen int) {
   749  
   750  	(*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) {
   751  		switch {
   752  		case strings.HasPrefix(verbPart, "?^"):
   753  			ce.sf.beforeStack = verbPart[2:]
   754  		case strings.HasPrefix(verbPart, "?$"):
   755  			ce.sf.afterStack = verbPart[2:]
   756  		default:
   757  			return false
   758  		}
   759  		return true
   760  	})
   761  
   762  	ce.formatParts = append(ce.formatParts, _CICE_FormatPart{
   763  		typ: _CICE_FPT_VERB_STACKTRACE,
   764  	})
   765  
   766  	return 2048
   767  }
   768  
   769  func (ce *CI_ConsoleEncoder) encodeJustText(to []byte, fp _CICE_FormatPart) []byte {
   770  	return bufw(to, fp.value)
   771  }
   772  
   773  func (ce *CI_ConsoleEncoder) encodeLevel(to []byte, fp _CICE_FormatPart, e *Entry) []byte {
   774  
   775  	formattedLevel := ""
   776  	switch fp.typ.Data() {
   777  
   778  	case _CICE_LF_NUMBER:
   779  		formattedLevel = strconv.Itoa(int(e.Level))
   780  	case _CICE_LF_SHORT_NORMAL:
   781  		formattedLevel = e.Level.String3()
   782  	case _CICE_LF_SHORT_UPPER_CASE:
   783  		formattedLevel = e.Level.ToUpper3()
   784  	case _CICE_LF_FULL_NORMAL:
   785  		formattedLevel = e.Level.String()
   786  	case _CICE_LF_FULL_UPPER_CASE:
   787  		formattedLevel = strings.ToUpper(e.Level.String())
   788  	}
   789  
   790  	return bufw(to, formattedLevel)
   791  }
   792  
   793  func (ce *CI_ConsoleEncoder) encodeTime(e *Entry, fp _CICE_FormatPart, to []byte) []byte {
   794  
   795  	formattedTime := ""
   796  
   797  	switch fp.typ.Data() {
   798  	case _CICE_TF_TIMESTAMP:
   799  		formattedTime = strconv.FormatInt(e.Time.Unix(), 10)
   800  	case _CICE_TF_ANSIC:
   801  		formattedTime = e.Time.Format(time.ANSIC)
   802  	case _CICE_TF_UNIXDATE:
   803  		formattedTime = e.Time.Format(time.UnixDate)
   804  	case _CICE_TF_RUBYDATE:
   805  		formattedTime = e.Time.Format(time.RubyDate)
   806  	case _CICE_TF_RFC822:
   807  		formattedTime = e.Time.Format(time.RFC822)
   808  	case _CICE_TF_RFC822_Z:
   809  		formattedTime = e.Time.Format(time.RFC822Z)
   810  	case _CICE_TF_RFC850:
   811  		formattedTime = e.Time.Format(time.RFC850)
   812  	case _CICE_TF_RFC1123:
   813  		formattedTime = e.Time.Format(time.RFC1123)
   814  	case _CICE_TF_RFC1123_Z:
   815  		formattedTime = e.Time.Format(time.RFC1123Z)
   816  	case _CICE_TF_RFC3339:
   817  		formattedTime = e.Time.Format(time.RFC3339)
   818  	default:
   819  		formattedTime = e.Time.Format(fp.value)
   820  	}
   821  
   822  	return bufw(to, formattedTime)
   823  }
   824  
   825  func (ce *CI_ConsoleEncoder) encodeColor(to []byte, fp _CICE_FormatPart) []byte {
   826  	return bufw(to, fp.value)
   827  }
   828  
   829  func (ce *CI_ConsoleEncoder) encodeColorForLevel(to []byte, e *Entry) []byte {
   830  	if color := ce.colorMap[e.Level]; color != "" {
   831  		return bufw(to, color)
   832  	}
   833  	return to
   834  }
   835  
   836  func (ce *CI_ConsoleEncoder) encodeBody(to []byte, e *Entry) []byte {
   837  
   838  	body := e.LogLetter.Messages[0].Body
   839  	if body == "" {
   840  		return to
   841  	}
   842  
   843  	if ce.bf.beforeBody != "" {
   844  		to = bufw(to, ce.bf.beforeBody)
   845  	}
   846  
   847  	to = bufw(to, body)
   848  
   849  	if ce.bf.afterBody != "" {
   850  		to = bufw(to, ce.bf.afterBody)
   851  	}
   852  
   853  	return to
   854  }
   855  
   856  func (ce *CI_ConsoleEncoder) encodeCaller(to []byte, e *Entry) []byte {
   857  
   858  	var frame *ekasys.StackFrame
   859  
   860  	switch {
   861  	case len(e.LogLetter.StackTrace) > 0:
   862  		frame = &e.LogLetter.StackTrace[0]
   863  
   864  	case e.ErrLetter != nil:
   865  		frame = &e.ErrLetter.StackTrace[0]
   866  
   867  	default:
   868  		return to
   869  	}
   870  
   871  	return ce.encodeStackFrame(to, frame, nil, ekaletter.LetterMessage{})
   872  }
   873  
   874  func (ce *CI_ConsoleEncoder) encodeFields(to []byte, fs, addFs []ekaletter.LetterField, isErrors, addPreEncoded bool) []byte {
   875  
   876  	if len(fs) == 0 && len(addFs) == 0 {
   877  		return to
   878  	}
   879  
   880  	if !isErrors && ce.ff.beforeFields != "" {
   881  		to = bufw(to, ce.ff.beforeFields)
   882  	}
   883  
   884  	var (
   885  		unnamedFieldIdx, writtenFields int16
   886  	)
   887  
   888  	addField := func(to []byte, f *ekaletter.LetterField, isErrors bool, unnamedFieldIex, writtenFields *int16) []byte {
   889  		if strings.HasPrefix(f.Key, "sys.") {
   890  			return to
   891  		}
   892  
   893  		keyBak := f.Key
   894  
   895  		if f.Key == "" && !f.IsSystem() {
   896  			f.Key = f.KeyOrUnnamed(&unnamedFieldIdx)
   897  		}
   898  
   899  		toLenBak := len(to)
   900  		to = ce.encodeField(to, *f, isErrors, *writtenFields)
   901  		if len(to) != toLenBak {
   902  			*writtenFields++
   903  		}
   904  
   905  		f.Key = keyBak
   906  		return to
   907  	}
   908  
   909  	for i, n := int16(0), int16(len(fs)); i < n; i++ {
   910  		to = addField(to, &fs[i], isErrors, &unnamedFieldIdx, &writtenFields)
   911  	}
   912  	for i, n := int16(0), int16(len(addFs)); i < n; i++ {
   913  		to = addField(to, &addFs[i], isErrors, &unnamedFieldIdx, &writtenFields)
   914  	}
   915  
   916  	if addPreEncoded && ce.preEncodedFieldsWritten > 0 {
   917  		if ce.ff.afterValue != "" {
   918  			to = to[:len(to)-len(ce.ff.afterValue)]
   919  		}
   920  		if l := len(to); to[l-1] != '\n' {
   921  			to = bufw(to, "\n")
   922  		}
   923  		to = bufw2(to, ce.preEncodedFields)
   924  	}
   925  
   926  	if writtenFields > 0 {
   927  
   928  		// remove last "after value" and write "after fields"
   929  		if ce.ff.afterValue != "" {
   930  			to = to[:len(to)-len(ce.ff.afterValue)]
   931  		}
   932  
   933  		if !isErrors && ce.ff.afterFields != "" {
   934  			to = bufw(to, ce.ff.afterFields)
   935  		}
   936  
   937  	} else {
   938  
   939  		// no fields were written
   940  		// remove 'beforeFields' part
   941  		to = to[:len(to)-len(ce.ff.beforeFields)]
   942  	}
   943  
   944  	return to
   945  }
   946  
   947  func (ce *CI_ConsoleEncoder) encodeField(to []byte, f ekaletter.LetterField, isErrors bool, fieldNum int16) []byte {
   948  
   949  	if f.IsSystem() && f.BaseType() == ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_ID {
   950  		return to
   951  	}
   952  
   953  	// Maybe field wants to be started with new line?
   954  	oldKey := f.Key
   955  	if f.Key = strings.TrimSpace(f.Key); len(oldKey) != len(f.Key) {
   956  		to = bufw(to, "\n")
   957  	}
   958  
   959  	// write new line and new line title
   960  	if ce.ff.itemsPerLine > 0 && fieldNum != 0 && fieldNum%ce.ff.itemsPerLine == 0 {
   961  		to = bufw(to, "\n")
   962  	}
   963  
   964  	if wasNewLine := to[len(to)-1] == '\n'; wasNewLine && !isErrors && len(ce.ff.afterNewLine) > 0 {
   965  		to = bufw(to, ce.ff.afterNewLine)
   966  	} else if wasNewLine && isErrors && len(ce.ff.afterNewLineForError) > 0 {
   967  		to = bufw(to, ce.ff.afterNewLineForError)
   968  	}
   969  
   970  	// Write "before key", key, "after key", value and "after value"
   971  
   972  	if ce.ff.beforeKey != "" {
   973  		to = bufw(to, ce.ff.beforeKey)
   974  	}
   975  	to = bufw(to, f.Key)
   976  	if ce.ff.afterKey != "" {
   977  		to = bufw(to, ce.ff.afterKey)
   978  	}
   979  	to = ce.encodeFieldValue(to, f)
   980  	if ce.ff.afterValue != "" {
   981  		to = bufw(to, ce.ff.afterValue)
   982  	}
   983  
   984  	return to
   985  }
   986  
   987  func (ce *CI_ConsoleEncoder) encodeFieldValue(to []byte, f ekaletter.LetterField) []byte {
   988  
   989  	if f.Kind.IsSystem() {
   990  		switch f.Kind.BaseType() {
   991  
   992  		case ekaletter.KIND_SYS_TYPE_EKAERR_UUID, ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_NAME:
   993  			to = bufw(to, `"`)
   994  			to = bufw(to, f.SValue)
   995  			to = bufw(to, `"`)
   996  
   997  		case ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_ID:
   998  			to = strconv.AppendInt(to, f.IValue, 10)
   999  
  1000  		default:
  1001  			to = bufw(to, `"<unsupported system field>"`)
  1002  		}
  1003  
  1004  	} else if f.Kind.IsNil() {
  1005  		to = bufw(to, "null")
  1006  
  1007  	} else if f.Kind.IsInvalid() {
  1008  		to = bufw(to, "<invalid_field>")
  1009  
  1010  	} else {
  1011  		switch f.Kind.BaseType() {
  1012  
  1013  		case ekaletter.KIND_TYPE_BOOL:
  1014  			to = strconv.AppendBool(to, f.IValue != 0)
  1015  
  1016  		case ekaletter.KIND_TYPE_INT,
  1017  			ekaletter.KIND_TYPE_INT_8, ekaletter.KIND_TYPE_INT_16,
  1018  			ekaletter.KIND_TYPE_INT_32, ekaletter.KIND_TYPE_INT_64:
  1019  			to = strconv.AppendInt(to, f.IValue, 10)
  1020  
  1021  		case ekaletter.KIND_TYPE_UINT,
  1022  			ekaletter.KIND_TYPE_UINT_8, ekaletter.KIND_TYPE_UINT_16,
  1023  			ekaletter.KIND_TYPE_UINT_32, ekaletter.KIND_TYPE_UINT_64:
  1024  			to = strconv.AppendUint(to, uint64(f.IValue), 10)
  1025  
  1026  		case ekaletter.KIND_TYPE_FLOAT_32:
  1027  			f := float64(math.Float32frombits(uint32(f.IValue)))
  1028  			to = strconv.AppendFloat(to, f, 'f', 2, 32)
  1029  
  1030  		case ekaletter.KIND_TYPE_FLOAT_64:
  1031  			f := math.Float64frombits(uint64(f.IValue))
  1032  			to = strconv.AppendFloat(to, f, 'f', 2, 64)
  1033  
  1034  		case ekaletter.KIND_TYPE_UINTPTR, ekaletter.KIND_TYPE_ADDR:
  1035  			to = bufw(to, "0x")
  1036  			to = strconv.AppendInt(to, f.IValue, 16)
  1037  
  1038  		case ekaletter.KIND_TYPE_STRING:
  1039  			to = strconv.AppendQuote(to, f.SValue)
  1040  
  1041  		case ekaletter.KIND_TYPE_COMPLEX_64:
  1042  			r := math.Float32frombits(uint32(f.IValue >> 32))
  1043  			i := math.Float32frombits(uint32(f.IValue))
  1044  			c := complex128(complex(r, i))
  1045  			// TODO: Use strconv.AppendComplex() when it will be released.
  1046  			to = bufw(to, strconv.FormatComplex(c, 'f', 2, 32))
  1047  
  1048  		case ekaletter.KIND_TYPE_COMPLEX_128:
  1049  			c := f.Value.(complex128)
  1050  			// TODO: Use strconv.AppendComplex() when it will be released.
  1051  			to = bufw(to, strconv.FormatComplex(c, 'f', 2, 64))
  1052  
  1053  		case ekaletter.KIND_TYPE_UNIX:
  1054  			to = bufw(to, time.Unix(f.IValue, 0).Format("Jan 2 15:04:05"))
  1055  
  1056  		case ekaletter.KIND_TYPE_UNIX_NANO:
  1057  			to = bufw(to, time.Unix(0, f.IValue).Format("Jan 2 15:04:05.000000000"))
  1058  
  1059  		case ekaletter.KIND_TYPE_DURATION:
  1060  			to = bufw(to, time.Duration(f.IValue).String())
  1061  
  1062  		case ekaletter.KIND_TYPE_MAP, ekaletter.KIND_TYPE_EXTMAP:
  1063  			// TODO: Add support of extracted maps.
  1064  			if jsonedMap, legacyErr := jsoniter.Marshal(f.Value); legacyErr == nil {
  1065  				to = bufw2(to, jsonedMap)
  1066  			} else {
  1067  				to = bufw(to, "<unsupported_map>")
  1068  			}
  1069  
  1070  		case ekaletter.KIND_TYPE_STRUCT:
  1071  			if jsonedStruct, legacyErr := jsoniter.Marshal(f.Value); legacyErr == nil {
  1072  				to = bufw2(to, jsonedStruct)
  1073  			} else {
  1074  				to = bufw(to, "<unsupported_struct>")
  1075  			}
  1076  
  1077  		case ekaletter.KIND_TYPE_ARRAY:
  1078  			if jsonedArray, legacyErr := jsoniter.Marshal(f.Value); legacyErr == nil {
  1079  				to = bufw2(to, jsonedArray)
  1080  			} else {
  1081  				to = bufw(to, "<unsupported_array>")
  1082  			}
  1083  
  1084  		default:
  1085  			to = bufw(to, "<unsupported_field>")
  1086  		}
  1087  	}
  1088  
  1089  	return to
  1090  }
  1091  
  1092  func (ce *CI_ConsoleEncoder) encodeStacktrace(to []byte, e *Entry) []byte {
  1093  
  1094  	trace := e.LogLetter.StackTrace
  1095  	isLightweightError := false
  1096  
  1097  	if len(trace) == 0 && e.ErrLetter != nil {
  1098  		trace = e.ErrLetter.StackTrace
  1099  		isLightweightError = len(trace) == 0
  1100  	}
  1101  
  1102  	n := int16(len(trace))
  1103  	if n == 0 && !isLightweightError {
  1104  		return to
  1105  	}
  1106  
  1107  	if ce.sf.beforeStack != "" {
  1108  		to = bufw(to, ce.sf.beforeStack)
  1109  	}
  1110  
  1111  	var (
  1112  		fi = 0 // fi for fields' index
  1113  		mi = 0 // mi for messages' index
  1114  
  1115  		fields   []ekaletter.LetterField   // We don't use log's fields and messages here
  1116  		messages []ekaletter.LetterMessage // 'cause they will be written in a different place.
  1117  	)
  1118  
  1119  	if e.ErrLetter != nil {
  1120  		fields = e.ErrLetter.Fields
  1121  		messages = e.ErrLetter.Messages
  1122  	}
  1123  
  1124  	// Simulate stacktrace's length if it's a lightweight error.
  1125  
  1126  	if isLightweightError {
  1127  
  1128  		var fieldGreatestFrameIdx int16 = 0
  1129  		if nf := len(fields); nf > 0 {
  1130  			fieldGreatestFrameIdx = fields[nf-1].StackFrameIdx
  1131  		}
  1132  
  1133  		var messageGreatestFrameIdx int16 = 0
  1134  		if nm := len(messages); nm > 0 {
  1135  			messageGreatestFrameIdx = messages[nm-1].StackFrameIdx
  1136  		}
  1137  
  1138  		n = ekamath.Max(fieldGreatestFrameIdx, messageGreatestFrameIdx)
  1139  	}
  1140  
  1141  	for i := int16(0); i < n; i++ {
  1142  		messageForFrame := ekaletter.LetterMessage{}
  1143  		fieldsForFrame := []ekaletter.LetterField(nil)
  1144  		fiEnd := 0
  1145  
  1146  		//goland:noinspection GoNilness
  1147  		if mi < len(messages) && messages[mi].StackFrameIdx == i {
  1148  			messageForFrame = messages[mi]
  1149  			mi++
  1150  		}
  1151  
  1152  		if fi < len(fields) && fields[fi].StackFrameIdx == i {
  1153  			fiEnd = fi + 1
  1154  			for fiEnd < len(fields) && fields[fiEnd].StackFrameIdx == i {
  1155  				fiEnd++
  1156  			}
  1157  		}
  1158  
  1159  		if fiEnd != 0 {
  1160  			fieldsForFrame = fields[fi:fiEnd]
  1161  		}
  1162  
  1163  		var frame *ekasys.StackFrame = nil
  1164  		if !isLightweightError {
  1165  			frame = &trace[i]
  1166  		}
  1167  
  1168  		to = ce.encodeStackFrame(to, frame, fieldsForFrame, messageForFrame)
  1169  	}
  1170  
  1171  	if nt := len(to) - 1; to[nt] == '\n' {
  1172  		to = to[:nt]
  1173  	}
  1174  
  1175  	if ce.sf.afterStack != "" {
  1176  		to = bufw(to, ce.sf.afterStack)
  1177  	}
  1178  
  1179  	return to
  1180  }
  1181  
  1182  func (ce *CI_ConsoleEncoder) encodeStackFrame(
  1183  
  1184  	to []byte,
  1185  	frame *ekasys.StackFrame,
  1186  	fields []ekaletter.LetterField,
  1187  	message ekaletter.LetterMessage,
  1188  
  1189  ) []byte {
  1190  
  1191  	lToAtStart := len(to)
  1192  
  1193  	switch {
  1194  	case frame == nil:
  1195  		// It's a lightweight error's frame. Do nothing.
  1196  
  1197  	case !ce.cf.isDefault:
  1198  		// Reminder: frame.DoFormat does once:
  1199  		// "<package>/<func> (<short_file>:<file_line>) <full_package_path>".
  1200  
  1201  		for i, n := 0, len(ce.cf.parts); i < n && ce.cf.parts[i].typ != 0; i++ {
  1202  			switch ce.cf.parts[i].typ {
  1203  
  1204  			case _CICE_CF_TYPE_SEPARATOR:
  1205  				to = bufw(to, ce.cf.parts[i].val)
  1206  
  1207  			case _CICE_CF_TYPE_FUNC_SHORT:
  1208  				frame.DoFormat()
  1209  				to = bufw(to, frame.Format[:frame.FormatFileOffset-1])
  1210  
  1211  			case _CICE_CF_TYPE_FUNC_FULL:
  1212  				to = bufw(to, frame.Function)
  1213  
  1214  			case _CICE_CF_TYPE_FILE_SHORT:
  1215  				frame.DoFormat()
  1216  				i := strings.IndexByte(frame.Format, ':')
  1217  				to = bufw(to, frame.Format[frame.FormatFileOffset+1:i])
  1218  
  1219  			case _CICE_CF_TYPE_FILE_FULL:
  1220  				to = bufw(to, frame.File)
  1221  
  1222  			case _CICE_CF_TYPE_LINE_NUM:
  1223  				to = bufw(to, strconv.Itoa(frame.Line))
  1224  
  1225  			case _CICE_CF_TYPE_PKG_FULL:
  1226  				to = bufw(to, frame.Format[frame.FormatFullPathOffset:])
  1227  			}
  1228  		}
  1229  
  1230  	default:
  1231  		to = bufw(to, frame.DoFormat())
  1232  	}
  1233  
  1234  	if message.Body != "" || len(fields) > 0 {
  1235  
  1236  		if frame != nil {
  1237  			to = bufwc(to, '\n')
  1238  		}
  1239  
  1240  		if ce.ff.afterNewLineForError != "" {
  1241  			to = bufw(to, ce.ff.afterNewLineForError)
  1242  		}
  1243  
  1244  		if message.Body != "" {
  1245  			to = bufw(to, message.Body)
  1246  			to = bufwc(to, '\n')
  1247  		}
  1248  
  1249  		lToBefore := len(to)
  1250  		to = ce.encodeFields(to, fields, nil, true, false)
  1251  
  1252  		// ce.encodeFields may write no fields. Then we must clear last "\n"
  1253  		if len(to) == lToBefore {
  1254  			to = to[:len(to)-1]
  1255  		}
  1256  	}
  1257  
  1258  	if lToAtStart != len(to) {
  1259  		to = bufwc(to, '\n')
  1260  	}
  1261  
  1262  	return to
  1263  }
  1264  
  1265  func (dc *_CICE_DropColors) Write(p []byte) (n int, err error) {
  1266  	if n, err = dc.buf.Write(p); err != nil && err != io.EOF {
  1267  		return n, err
  1268  	}
  1269  	b := dc.buf.Bytes()
  1270  	j := 0
  1271  	for i, n, write := 0, len(b), true; i < n; i++ {
  1272  		if write && b[i] == '\033' {
  1273  			write = false
  1274  			i += 2
  1275  		} else if !write && b[i] == 'm' {
  1276  			write = true
  1277  		} else if write {
  1278  			b[j] = b[i]
  1279  			j++
  1280  		}
  1281  	}
  1282  	return dc.dest.Write(b[:j])
  1283  }