github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/throw/fmtwrap.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package throw
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  	"sync/atomic"
    12  )
    13  
    14  var panicFmtFn atomic.Value
    15  
    16  type FormatterFunc func(string, interface{}) (msg string, extra interface{}, includeExtra bool)
    17  
    18  func GetFormatter() FormatterFunc {
    19  	if vv, ok := panicFmtFn.Load().(FormatterFunc); ok {
    20  		return vv
    21  	}
    22  	return nil
    23  }
    24  
    25  // SetFormatter sets a formatter to be applied by NewDescription and New
    26  func SetFormatter(fn FormatterFunc) {
    27  	if fn == nil {
    28  		panic(IllegalValue())
    29  	}
    30  	panicFmtFn.Store(fn)
    31  }
    32  
    33  // NewDescription creates an error with the given description. Log structs can be used
    34  func NewDescription(description interface{}) error {
    35  	if description == nil {
    36  		return nil
    37  	}
    38  	if fmtFn := GetFormatter(); fmtFn != nil {
    39  		return _wrap(fmtFn("", description))
    40  	}
    41  	if err, ok := description.(error); ok {
    42  		return err
    43  	}
    44  	return _wrapDesc(description)
    45  }
    46  
    47  // New creates an error with the given message text and description. Log structs can be used
    48  func New(msg string, description ...interface{}) error {
    49  	return Severe(0, msg, description...)
    50  }
    51  
    52  func Severe(severity Severity, msg string, description ...interface{}) error {
    53  	if description == nil && msg == "" && severity == 0 {
    54  		return nil
    55  	}
    56  	if fmtFn := GetFormatter(); fmtFn != nil {
    57  		return _wrap(fmtFn(msg, description))
    58  	}
    59  
    60  	var err error
    61  	if len(description) > 0 {
    62  		d0 := description[0]
    63  		s := ""
    64  		if vv, ok := d0.(logStringer); ok {
    65  			s = vv.LogString()
    66  		} else {
    67  			s = defaultFmt(d0, false)
    68  		}
    69  		err = fmtWrap{msg: msg, severity: severity, extra: d0, useExtra: msg != s}
    70  	} else {
    71  		err = fmtWrap{msg: msg, severity: severity}
    72  	}
    73  
    74  	for i := 1; i < len(description); i++ {
    75  		if description[i] == nil {
    76  			continue
    77  		}
    78  		err = WithDetails(err, description[i])
    79  	}
    80  
    81  	return err
    82  }
    83  
    84  func NewFmt(msg string, errorDescription interface{}, fmtFn FormatterFunc) error {
    85  	if fmtFn == nil {
    86  		panic(IllegalValue())
    87  	}
    88  	if errorDescription == nil && msg == "" {
    89  		return nil
    90  	}
    91  	return _wrap(fmtFn(msg, errorDescription))
    92  }
    93  
    94  func wrapInternal(errorDescription interface{}) fmtWrap {
    95  	if fmtFn := GetFormatter(); fmtFn != nil {
    96  		return _wrap(fmtFn("", errorDescription))
    97  	}
    98  	return _wrapDesc(errorDescription)
    99  }
   100  
   101  func _wrapDesc(errorDescription interface{}) fmtWrap {
   102  	if vv, ok := errorDescription.(logStringer); ok {
   103  		msg := vv.LogString()
   104  		return fmtWrap{msg: msg, extra: errorDescription}
   105  	}
   106  	msg := defaultFmt(errorDescription, true)
   107  	return fmtWrap{msg: msg, extra: errorDescription}
   108  }
   109  
   110  func _wrap(msg string, extra interface{}, useExtra bool) fmtWrap {
   111  	return fmtWrap{msg: msg, extra: extra, useExtra: useExtra}
   112  }
   113  
   114  type extraInfo interface {
   115  	ExtraInfo() (string, Severity, interface{})
   116  }
   117  
   118  func UnwrapExtraInfo(err interface{}) (string, Severity, interface{}, bool) {
   119  	if e, ok := err.(extraInfo); ok {
   120  		st, sv, ei := e.ExtraInfo()
   121  		return st, sv, ei, true
   122  	}
   123  	return "", 0, nil, false
   124  }
   125  
   126  type logStringer interface{ LogString() string }
   127  
   128  func defaultFmt(v interface{}, force bool) string {
   129  	switch vv := v.(type) {
   130  	case fmt.Stringer:
   131  		return vv.String()
   132  	case error:
   133  		return vv.Error()
   134  	case string:
   135  		return vv
   136  	case *string:
   137  		if vv != nil {
   138  			return *vv
   139  		}
   140  	case nil:
   141  		//
   142  	default:
   143  		s := ""
   144  		t := reflect.TypeOf(v)
   145  		switch {
   146  		case t.Kind() == reflect.Struct && t.PkgPath() == "":
   147  			return fmt.Sprintf("%+v", vv)
   148  		case force:
   149  			s = fmt.Sprint(vv)
   150  		default:
   151  			return ""
   152  		}
   153  		if len(s) == 0 {
   154  			s = fmt.Sprintf("%T(%[1]p)", vv)
   155  		}
   156  		return s
   157  	}
   158  	return "<nil>"
   159  }