go.temporal.io/server@v1.23.0/common/testing/protoassert/pretty_print.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package protoassert 26 27 import ( 28 "fmt" 29 "reflect" 30 "regexp" 31 "strings" 32 ) 33 34 var isPublic = regexp.MustCompile("^[A-Z]") 35 36 // pretty-print the public state of the proto.Message. we don't use prettyPrint the way testify does 37 // as the private state isn't worth comparing; it's all for serialization. 38 // 39 // This is custom and not from testify. 40 func prettyPrint(values any) string { 41 var b strings.Builder 42 prettyPrintAny(&b, reflect.ValueOf(values), 0) 43 return b.String() 44 } 45 46 func prettyPrintAny(b *strings.Builder, v reflect.Value, depth int) { 47 switch v.Type().Kind() { 48 case reflect.Array: 49 prettyPrintSlice(b, v, depth) 50 case reflect.Chan: 51 panic("Not implemented") 52 case reflect.Func: 53 panic("Not implemented") 54 case reflect.Interface: 55 prettyPrintStruct(b, v.Elem(), depth) 56 case reflect.Map: 57 prettyPrintMap(b, v, depth) 58 case reflect.Pointer: 59 if v.IsValid() { 60 if v.IsNil() { 61 b.WriteString("nil") 62 } else { 63 b.WriteByte('&') 64 prettyPrintStruct(b, v.Elem(), depth) 65 } 66 } 67 case reflect.Slice: 68 prettyPrintSlice(b, v, depth) 69 case reflect.Struct: 70 prettyPrintStruct(b, v, depth) 71 default: 72 fmt.Fprintf(b, "%v", v.Interface()) 73 } 74 75 } 76 77 func prettyPrintMap(b *strings.Builder, v reflect.Value, depth int) { 78 fmt.Fprintf(b, "map[%s]%s", v.Type().Key().Name(), v.Type().Elem().Name()) 79 if v.Len() == 0 { 80 b.WriteString("{}") 81 } 82 83 b.WriteByte('{') 84 iter := v.MapRange() 85 for iter.Next() { 86 indent(b, depth+1) 87 88 prettyPrintAny(b, iter.Key(), depth+1) 89 b.WriteString(": ") 90 prettyPrintAny(b, iter.Value(), depth+1) 91 } 92 indent(b, depth) 93 b.WriteByte('\n') 94 } 95 96 func prettyPrintSlice(b *strings.Builder, v reflect.Value, depth int) { 97 if v.Len() == 0 { 98 b.WriteString("[]") 99 } 100 b.WriteByte('[') 101 for j := 0; j < v.Len(); j++ { 102 indent(b, depth+1) 103 prettyPrintAny(b, v.Index(j), depth+1) 104 } 105 indent(b, depth) 106 b.WriteByte(']') 107 } 108 109 func prettyPrintStruct(b *strings.Builder, v reflect.Value, depth int) { 110 ty := v.Type() 111 b.WriteString(ty.Name()) 112 if ty.NumField() == 0 { 113 b.WriteString("{}") 114 return 115 } 116 117 b.WriteString("{") 118 for i := 0; i < ty.NumField(); i++ { 119 f := ty.Field(i) 120 vf := v.Field(i) 121 if !isPublic.MatchString(f.Name) { 122 continue 123 } 124 indent(b, depth+1) 125 b.WriteString(f.Name) 126 b.WriteString(": ") 127 prettyPrintAny(b, vf, depth+1) 128 } 129 indent(b, depth) 130 b.WriteString("}") 131 } 132 133 func indent(b *strings.Builder, depth int) { 134 b.WriteByte('\n') 135 for i := 0; i < depth; i++ { 136 b.WriteString(" ") 137 } 138 }