package cmp
import (
)
var (
anyType = reflect.TypeOf((*interface{})(nil)).Elem()
stringType = reflect.TypeOf((*string)(nil)).Elem()
bytesType = reflect.TypeOf((*[]byte)(nil)).Elem()
byteType = reflect.TypeOf((*byte)(nil)).Elem()
)
type formatValueOptions struct {
AvoidStringer bool
PrintAddresses bool
QualifiedNames bool
VerbosityLevel int
LimitVerbosity bool
}
func ( formatOptions) ( reflect.Type, textNode) textNode {
switch .TypeMode {
case autoType:
switch .Kind() {
case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
if .Equal(textNil) {
return
}
default:
return
}
if .DiffMode == diffIdentical {
return
}
case elideType:
return
}
:= value.TypeString(, .QualifiedNames)
if .Name() == "" {
switch .Kind() {
case reflect.Chan, reflect.Func, reflect.Ptr:
= "(" + + ")"
}
}
return &textWrap{Prefix: , Value: wrapParens()}
}
func ( textNode) textNode {
var *textWrap
if , := .(*textWrap); {
switch .Metadata.(type) {
case leafReference, trunkReference, trunkReferences:
=
if , := .Value.(*textWrap); {
=
}
}
:= strings.HasPrefix(.Prefix, "(") && strings.HasSuffix(.Suffix, ")")
:= strings.HasPrefix(.Prefix, "{") && strings.HasSuffix(.Suffix, "}")
if || {
return
}
}
if != nil {
.Value = &textWrap{Prefix: "(", Value: .Value, Suffix: ")"}
return
}
return &textWrap{Prefix: "(", Value: , Suffix: ")"}
}
func ( formatOptions) ( reflect.Value, reflect.Kind, *pointerReferences) ( textNode) {
if !.IsValid() {
return nil
}
:= .Type()
if == reflect.Slice {
, := .Push(.Addr())
if {
return makeLeafReference(, false)
}
defer .Pop()
defer func() { = wrapTrunkReference(, false, ) }()
}
if !.AvoidStringer && .CanInterface() {
if (.Kind() != reflect.Ptr && .Kind() != reflect.Interface) || !.IsNil() {
var , string
func() {
defer func() { recover() }()
switch v := .Interface().(type) {
case error:
= .Error()
= "e"
case fmt.Stringer:
= .String()
= "s"
}
}()
if != "" {
return .formatString(, )
}
}
}
var bool
defer func() {
if ! {
= .FormatType(, )
}
}()
switch .Kind() {
case reflect.Bool:
return textLine(fmt.Sprint(.Bool()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return textLine(fmt.Sprint(.Int()))
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return textLine(fmt.Sprint(.Uint()))
case reflect.Uint8:
if == reflect.Slice || == reflect.Array {
return textLine(formatHex(.Uint()))
}
return textLine(fmt.Sprint(.Uint()))
case reflect.Uintptr:
return textLine(formatHex(.Uint()))
case reflect.Float32, reflect.Float64:
return textLine(fmt.Sprint(.Float()))
case reflect.Complex64, reflect.Complex128:
return textLine(fmt.Sprint(.Complex()))
case reflect.String:
return .formatString("", .String())
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
return textLine(formatPointer(value.PointerOf(), true))
case reflect.Struct:
var textList
:= makeAddressable()
:= .NumField()
if .LimitVerbosity {
= ((1 << .verbosity()) >> 1) << 2
.VerbosityLevel--
}
for := 0; < .NumField(); ++ {
:= .Field()
if .IsZero() {
continue
}
if len() == {
.AppendEllipsis(diffStats{})
break
}
:= .Field()
if supportExporters && !isExported(.Name) {
= retrieveUnexportedField(, , true)
}
:= .WithTypeMode(autoType).(, .Kind(), )
= append(, textRecord{Key: .Name, Value: })
}
return &textWrap{Prefix: "{", Value: , Suffix: "}"}
case reflect.Slice:
if .IsNil() {
return textNil
}
if .Elem() == byteType {
:= .Bytes()
:= func( rune) bool { return unicode.IsPrint() || unicode.IsSpace() }
if len() > 0 && utf8.Valid() && len(bytes.TrimFunc(, )) == 0 {
= .formatString("", string())
= true
return .FormatType(, )
}
}
fallthrough
case reflect.Array:
:= .Len()
if .LimitVerbosity {
= ((1 << .verbosity()) >> 1) << 2
.VerbosityLevel--
}
var textList
for := 0; < .Len(); ++ {
if len() == {
.AppendEllipsis(diffStats{})
break
}
:= .WithTypeMode(elideType).(.Index(), .Kind(), )
= append(, textRecord{Value: })
}
= &textWrap{Prefix: "{", Value: , Suffix: "}"}
if .Kind() == reflect.Slice && .PrintAddresses {
:= fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(), false), .Len(), .Cap())
= &textWrap{Prefix: pointerDelimPrefix + + pointerDelimSuffix, Value: }
}
return
case reflect.Map:
if .IsNil() {
return textNil
}
, := .Push()
if {
return makeLeafReference(, .PrintAddresses)
}
defer .Pop()
:= .Len()
if .LimitVerbosity {
= ((1 << .verbosity()) >> 1) << 2
.VerbosityLevel--
}
var textList
for , := range value.SortKeys(.MapKeys()) {
if len() == {
.AppendEllipsis(diffStats{})
break
}
:= formatMapKey(, false, )
:= .WithTypeMode(elideType).(.MapIndex(), .Kind(), )
= append(, textRecord{Key: , Value: })
}
= &textWrap{Prefix: "{", Value: , Suffix: "}"}
= wrapTrunkReference(, .PrintAddresses, )
return
case reflect.Ptr:
if .IsNil() {
return textNil
}
, := .Push()
if {
= makeLeafReference(, .PrintAddresses)
return &textWrap{Prefix: "&", Value: }
}
defer .Pop()
if .Type().Name() == "" {
= true
}
= .(.Elem(), .Kind(), )
= wrapTrunkReference(, .PrintAddresses, )
= &textWrap{Prefix: "&", Value: }
return
case reflect.Interface:
if .IsNil() {
return textNil
}
return .WithTypeMode(emitType).(.Elem(), .Kind(), )
default:
panic(fmt.Sprintf("%v kind not handled", .Kind()))
}
}
func ( formatOptions) (, string) textNode {
:= len()
:= strings.Count(, "\n") + 1
if .LimitVerbosity {
= (1 << .verbosity()) << 5
= (1 << .verbosity()) << 2
}
:= strings.Split(strings.TrimSuffix(, "\n"), "\n")
:= len() >= 4 && (.DiffMode == '-' || .DiffMode == '+')
for := 0; < len() && ; ++ {
[] = strings.TrimPrefix(strings.TrimSuffix([], "\r"), "\r")
:= func( rune) bool {
return unicode.IsPrint() || == '\t'
}
:= []
= !strings.HasPrefix(strings.TrimPrefix(, ), `"""`) && !strings.HasPrefix(, "...") && strings.TrimFunc(, ) == "" && len() <=
}
if {
var textList
= append(, textRecord{Diff: .DiffMode, Value: textLine( + `"""`), ElideComma: true})
for , := range {
if := len() - ; == -1 && > 1 {
:= commentString(fmt.Sprintf("%d elided lines", ))
= append(, textRecord{Diff: .DiffMode, Value: textEllipsis, ElideComma: true, Comment: })
break
}
= append(, textRecord{Diff: .DiffMode, Value: textLine(), ElideComma: true})
}
= append(, textRecord{Diff: .DiffMode, Value: textLine( + `"""`), ElideComma: true})
return &textWrap{Prefix: "(", Value: , Suffix: ")"}
}
if len() > +len(textEllipsis) {
return textLine( + formatString([:]) + string(textEllipsis))
}
return textLine( + formatString())
}
func ( reflect.Value, bool, *pointerReferences) string {
var formatOptions
.DiffMode = diffIdentical
.TypeMode = elideType
.PrintAddresses =
.AvoidStringer =
.QualifiedNames =
.VerbosityLevel = maxVerbosityPreset
.LimitVerbosity = true
:= .FormatValue(, reflect.Map, ).String()
return strings.TrimSpace()
}
func ( string) string {
:= strconv.Quote()
if len() == 1+len()+1 {
return
}
:= func( rune) bool {
return == '`' || == '\n' || !(unicode.IsPrint() || == '\t')
}
if utf8.ValidString() && strings.IndexFunc(, ) < 0 {
return "`" + + "`"
}
return
}
func ( uint64) string {
var string
switch {
case <= 0xff:
= "0x%02x"
case <= 0xffff:
= "0x%04x"
case <= 0xffffff:
= "0x%06x"
case <= 0xffffffff:
= "0x%08x"
case <= 0xffffffffff:
= "0x%010x"
case <= 0xffffffffffff:
= "0x%012x"
case <= 0xffffffffffffff:
= "0x%014x"
case <= 0xffffffffffffffff:
= "0x%016x"
}
return fmt.Sprintf(, )
}