From 6ba70ed046214de9b570a6257ff1c14f8b97647f Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Sat, 5 Jul 2025 18:47:08 -0400 Subject: [PATCH] internal/testutil: Create function to closely examine any data --- internal/testutil/testutil.go | 67 +++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go index 401c9a2..4295b92 100644 --- a/internal/testutil/testutil.go +++ b/internal/testutil/testutil.go @@ -3,6 +3,7 @@ package testutil import "fmt" import "slices" import "strings" +import "reflect" // Snake lets you compare blocks of data where the ordering of certain parts may // be swapped every which way. It is designed for comparing the encoding of @@ -92,3 +93,69 @@ func HexBytes(data []byte) string { } return out.String() } + +// Describe returns a string representing the type and data of the given value. +func Describe(value any) string { + desc := describer { } + desc.describe(reflect.ValueOf(value)) + return desc.String() +} + +type describer struct { + strings.Builder + indent int +} + +func (this *describer) describe(value reflect.Value) { + value = reflect.ValueOf(value.Interface()) + switch value.Kind() { + case reflect.Array, reflect.Slice: + this.printf("[\n") + this.indent += 1 + for index := 0; index < value.Len(); index ++ { + this.iprintf("") + this.describe(value.Index(index)) + this.iprintf("\n") + } + this.indent -= 1 + this.iprintf("]") + case reflect.Struct: + this.printf("struct {\n") + this.indent += 1 + typ := value.Type() + for index := range typ.NumField() { + indexBuffer := [1]int { index } + this.iprintf("%s: ", typ.Field(index).Name) + this.describe(value.FieldByIndex(indexBuffer[:])) + this.iprintf("\n") + } + this.indent -= 1 + this.iprintf("}\n") + case reflect.Map: + this.printf("map {\n") + this.indent += 1 + iter := value.MapRange() + for iter.Next() { + this.iprintf("") + this.describe(iter.Key()) + this.printf(": ") + this.describe(iter.Value()) + this.iprintf("\n") + } + this.indent -= 1 + this.iprintf("}\n") + case reflect.Pointer: + this.printf("& ") + this.describe(value.Elem()) + default: + this.printf("<%v %v>", value.Type(), value.Interface()) + } +} + +func (this *describer) printf(format string, v ...any) { + fmt.Fprintf(this, format, v...) +} + +func (this *describer) iprintf(format string, v ...any) { + fmt.Fprintf(this, strings.Repeat("\t", this.indent) + format, v...) +}