github.com/searKing/golang/go@v1.2.117/log/slog/text_handler.go (about)

     1  // Copyright 2023 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package slog
     6  
     7  import (
     8  	"encoding"
     9  	"encoding/json"
    10  	"fmt"
    11  	"log/slog"
    12  	"reflect"
    13  	"strconv"
    14  	"unicode"
    15  	"unicode/utf8"
    16  
    17  	"github.com/searKing/golang/go/unsafe"
    18  )
    19  
    20  func appendTextValue(s *handleState, v slog.Value) error {
    21  	switch v.Kind() {
    22  	case slog.KindString:
    23  		s.appendStringMayQuote(v.String())
    24  	case slog.KindTime:
    25  		s.appendTime(v.Time())
    26  	case slog.KindAny:
    27  		a := v.Any()
    28  		if tm, ok := a.(encoding.TextMarshaler); ok {
    29  			data, err := tm.MarshalText()
    30  			if err != nil {
    31  				return err
    32  			}
    33  			s.appendBytesMayQuote(data)
    34  			return nil
    35  		}
    36  		if err, ok := a.(error); ok && err != nil {
    37  			s.appendStringMayQuote(err.Error())
    38  			return nil
    39  		}
    40  		if tm, ok := a.(fmt.Stringer); ok {
    41  			s.appendStringMayQuote(tm.String())
    42  			return nil
    43  		}
    44  		if tm, ok := a.(json.Marshaler); ok {
    45  			data, err := tm.MarshalJSON()
    46  			if err != nil {
    47  				return err
    48  			}
    49  			s.appendBytesMayQuote(data)
    50  			return nil
    51  		}
    52  		if bs, ok := byteSlice(a); ok {
    53  			// As of Go 1.19, this only allocates for strings longer than 32 bytes.
    54  			s.buf.WriteString(strconv.Quote(unsafe.BytesToString(bs)))
    55  			return nil
    56  		}
    57  		s.appendStringMayQuote(fmt.Sprintf("%+v", v.Any()))
    58  	default:
    59  		s.appendStringMayQuote(v.String())
    60  	}
    61  	return nil
    62  }
    63  
    64  // byteSlice returns its argument as a []byte if the argument's
    65  // underlying type is []byte, along with a second return value of true.
    66  // Otherwise it returns nil, false.
    67  func byteSlice(a any) ([]byte, bool) {
    68  	if bs, ok := a.([]byte); ok {
    69  		return bs, true
    70  	}
    71  	// Like Printf's %s, we allow both the slice type and the byte element type to be named.
    72  	t := reflect.TypeOf(a)
    73  	if t != nil && t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
    74  		return reflect.ValueOf(a).Bytes(), true
    75  	}
    76  	return nil, false
    77  }
    78  
    79  func needsQuoting(s string, unprintableOnly bool) bool {
    80  	if len(s) == 0 {
    81  		return true
    82  	}
    83  	for i := 0; i < len(s); {
    84  		b := s[i]
    85  		if b < utf8.RuneSelf {
    86  			// Quote anything except a backslash that would need quoting in a
    87  			// JSON string, as well as space and '='
    88  			if unprintableOnly {
    89  				if !safeSet[b] {
    90  					return true
    91  				}
    92  			} else {
    93  				if b != '\\' && (b == ' ' || b == '=' || !safeSet[b]) {
    94  					return true
    95  				}
    96  			}
    97  			i++
    98  			continue
    99  		}
   100  		r, size := utf8.DecodeRuneInString(s[i:])
   101  		if unprintableOnly {
   102  			if r == utf8.RuneError || !unicode.IsPrint(r) {
   103  				return true
   104  			}
   105  		} else {
   106  			if r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r) {
   107  				return true
   108  			}
   109  		}
   110  		i += size
   111  	}
   112  	return false
   113  }