import repo, format without wrapping
This commit is contained in:
commit
79047a09dd
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.coverprofile
|
24
Makefile
Normal file
24
Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
SOURCES = $(shell find . -name "*.go")
|
||||||
|
|
||||||
|
default: build
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
check:
|
||||||
|
go test -count 1 ./...
|
||||||
|
|
||||||
|
imports:
|
||||||
|
@goimports -w $(SOURCES)
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
@gofmt -w -s $(SOURCES)
|
||||||
|
|
||||||
|
.coverprofile: $(SOURCES)
|
||||||
|
go test -count 1 -coverprofile .coverprofile
|
||||||
|
|
||||||
|
cover: .coverprofile
|
||||||
|
go tool cover -func .coverprofile
|
||||||
|
|
||||||
|
showcover: .coverprofile
|
||||||
|
go tool cover -html .coverprofile
|
60
notation.go
Normal file
60
notation.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package notation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type opts int
|
||||||
|
|
||||||
|
const none opts = 0
|
||||||
|
|
||||||
|
const (
|
||||||
|
wrap opts = 1 << iota
|
||||||
|
types
|
||||||
|
skipTypes
|
||||||
|
allTypes
|
||||||
|
)
|
||||||
|
|
||||||
|
func sprintValues(o opts, v []interface{}) string {
|
||||||
|
s := make([]string, len(v))
|
||||||
|
for i := range v {
|
||||||
|
if v[i] == nil {
|
||||||
|
s[i] = "nil"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s[i] = sprint(o, reflect.ValueOf(v[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
sep := " "
|
||||||
|
if o&wrap != 0 {
|
||||||
|
sep = "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(s, sep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sprint(v ...interface{}) string {
|
||||||
|
return sprintValues(none, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sprintw(v ...interface{}) string {
|
||||||
|
return sprintValues(wrap, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sprintt(v ...interface{}) string {
|
||||||
|
return sprintValues(types, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sprintwt(v ...interface{}) string {
|
||||||
|
return sprintValues(wrap|types, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sprintv(v ...interface{}) string {
|
||||||
|
return sprintValues(allTypes, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sprintwv(v ...interface{}) string {
|
||||||
|
return sprintValues(wrap|allTypes, v)
|
||||||
|
}
|
271
sprint.go
Normal file
271
sprint.go
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
package notation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func withType(o opts) (opts, bool, bool) {
|
||||||
|
if o&types == 0 && o&allTypes == 0 {
|
||||||
|
return o, false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if o&skipTypes != 0 && o&allTypes == 0 {
|
||||||
|
return o &^ skipTypes, false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return o, true, o&allTypes != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintNil(o opts, r reflect.Value) string {
|
||||||
|
if _, _, a := withType(o); !a {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s(nil)", sprintType(r.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintPrimitive(o opts, r reflect.Value, v interface{}, suppressType ...string) string {
|
||||||
|
s := fmt.Sprint(v)
|
||||||
|
if s[0] == '(' && s[len(s)-1] == ')' {
|
||||||
|
s = s[1 : len(s)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, w, a := withType(o)
|
||||||
|
if !w {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
t := sprintType(r.Type())
|
||||||
|
if !a {
|
||||||
|
for _, suppress := range suppressType {
|
||||||
|
if t == suppress {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s(%s)", t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintItems(o opts, prefix string, r reflect.Value) string {
|
||||||
|
o, w, _ := withType(o)
|
||||||
|
itemOpts := o | skipTypes
|
||||||
|
s := make([]string, r.Len())
|
||||||
|
for i := 0; i < r.Len(); i++ {
|
||||||
|
s[i] = sprint(itemOpts, r.Index(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !w {
|
||||||
|
return fmt.Sprintf("%s{%s}", prefix, strings.Join(s, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s{%s}", sprintType(r.Type()), strings.Join(s, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintHidden(o opts, r reflect.Value, hidden string) string {
|
||||||
|
if r.IsNil() {
|
||||||
|
return sprintNil(o, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, w, _ := withType(o)
|
||||||
|
if !w {
|
||||||
|
return hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintType(r.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintArray(o opts, r reflect.Value) string {
|
||||||
|
return sprintItems(o, fmt.Sprintf("[%d]", r.Len()), r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintChan(o opts, r reflect.Value) string {
|
||||||
|
return sprintHidden(o, r, "chan")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintFunc(o opts, r reflect.Value) string {
|
||||||
|
return sprintHidden(o, r, "func()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprinterface(o opts, r reflect.Value) string {
|
||||||
|
if r.IsNil() {
|
||||||
|
return sprintNil(o, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
o, w, _ := withType(o)
|
||||||
|
if !w {
|
||||||
|
return sprint(o, r.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s(%s)", sprintType(r.Type()), sprint(o, r.Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintMap(o opts, r reflect.Value) string {
|
||||||
|
if r.IsNil() {
|
||||||
|
return sprintNil(o, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
o, w, _ := withType(o)
|
||||||
|
itemOpts := o | skipTypes
|
||||||
|
var items []string
|
||||||
|
for _, key := range r.MapKeys() {
|
||||||
|
items = append(
|
||||||
|
items,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%s: %s",
|
||||||
|
sprint(itemOpts, key),
|
||||||
|
sprint(itemOpts, r.MapIndex(key)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(items)
|
||||||
|
sitems := strings.Join(items, ", ")
|
||||||
|
if !w {
|
||||||
|
return fmt.Sprintf("map{%s}", sitems)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s{%s}", sprintType(r.Type()), sitems)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintPointer(o opts, r reflect.Value) string {
|
||||||
|
if r.IsNil() {
|
||||||
|
return sprintNil(o, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := sprint(o, r.Elem())
|
||||||
|
if _, w, _ := withType(o); !w {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("*%s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintList(o opts, r reflect.Value) string {
|
||||||
|
if r.IsNil() {
|
||||||
|
return sprintNil(o, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintItems(o, "[]", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintString(o opts, r reflect.Value) string {
|
||||||
|
b := []byte(r.String())
|
||||||
|
e := make([]byte, 0, len(b))
|
||||||
|
for _, c := range b {
|
||||||
|
switch c {
|
||||||
|
case '\\':
|
||||||
|
e = append(e, '\\', '\\')
|
||||||
|
case '"':
|
||||||
|
e = append(e, '\\', '"')
|
||||||
|
case '\b':
|
||||||
|
e = append(e, '\\', 'b')
|
||||||
|
case '\f':
|
||||||
|
e = append(e, '\\', 'f')
|
||||||
|
case '\n':
|
||||||
|
e = append(e, '\\', 'n')
|
||||||
|
case '\r':
|
||||||
|
e = append(e, '\\', 'r')
|
||||||
|
case '\t':
|
||||||
|
e = append(e, '\\', 't')
|
||||||
|
case '\v':
|
||||||
|
e = append(e, '\\', 'v')
|
||||||
|
default:
|
||||||
|
e = append(e, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := fmt.Sprintf("\"%s\"", string(e))
|
||||||
|
_, w, a := withType(o)
|
||||||
|
if !w {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
t := sprintType(r.Type())
|
||||||
|
if !a && t == "string" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s(%s)", t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintStruct(o opts, r reflect.Value) string {
|
||||||
|
o, w, _ := withType(o)
|
||||||
|
fieldOpts := o | skipTypes
|
||||||
|
rt := r.Type()
|
||||||
|
f := make([]string, r.NumField())
|
||||||
|
for i := 0; i < r.NumField(); i++ {
|
||||||
|
name := rt.Field(i).Name
|
||||||
|
f[i] = fmt.Sprintf(
|
||||||
|
"%s: %s",
|
||||||
|
name,
|
||||||
|
sprint(fieldOpts, r.FieldByName(name)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := strings.Join(f, ", ")
|
||||||
|
if !w {
|
||||||
|
return fmt.Sprintf("{%s}", fs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s{%s}", sprintType(rt), fs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintUnsafePointer(o opts, r reflect.Value) string {
|
||||||
|
if r.IsNil() {
|
||||||
|
return sprintNil(o, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "pointer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprint(o opts, r reflect.Value) string {
|
||||||
|
switch r.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return sprintPrimitive(o, r, r.Bool(), "bool")
|
||||||
|
case
|
||||||
|
reflect.Int,
|
||||||
|
reflect.Int8,
|
||||||
|
reflect.Int16,
|
||||||
|
reflect.Int32,
|
||||||
|
reflect.Int64:
|
||||||
|
return sprintPrimitive(o, r, r.Int(), "int")
|
||||||
|
case
|
||||||
|
reflect.Uint,
|
||||||
|
reflect.Uint8,
|
||||||
|
reflect.Uint16,
|
||||||
|
reflect.Uint32,
|
||||||
|
reflect.Uint64,
|
||||||
|
reflect.Uintptr:
|
||||||
|
return sprintPrimitive(o, r, r.Uint())
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return sprintPrimitive(o, r, r.Float())
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
return sprintPrimitive(o, r, r.Complex())
|
||||||
|
case reflect.Array:
|
||||||
|
return sprintArray(o, r)
|
||||||
|
case reflect.Chan:
|
||||||
|
return sprintChan(o, r)
|
||||||
|
case reflect.Func:
|
||||||
|
return sprintFunc(o, r)
|
||||||
|
case reflect.Interface:
|
||||||
|
return sprinterface(o, r)
|
||||||
|
case reflect.Map:
|
||||||
|
return sprintMap(o, r)
|
||||||
|
case reflect.Ptr:
|
||||||
|
return sprintPointer(o, r)
|
||||||
|
case reflect.Slice:
|
||||||
|
return sprintList(o, r)
|
||||||
|
case reflect.String:
|
||||||
|
return sprintString(o, r)
|
||||||
|
case reflect.Struct:
|
||||||
|
return sprintStruct(o, r)
|
||||||
|
case reflect.UnsafePointer:
|
||||||
|
return sprintUnsafePointer(o, r)
|
||||||
|
default:
|
||||||
|
return "<invalid>"
|
||||||
|
}
|
||||||
|
}
|
264
sprint_test.go
Normal file
264
sprint_test.go
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
package notation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSprint(t *testing.T) {
|
||||||
|
type (
|
||||||
|
myBool bool
|
||||||
|
myInt int
|
||||||
|
myFloat float64
|
||||||
|
myComplex complex64
|
||||||
|
myArray [3]int
|
||||||
|
myChannel chan int
|
||||||
|
myFunction func(int, int) int
|
||||||
|
myMap map[int]int
|
||||||
|
myPointer *int
|
||||||
|
myList []int
|
||||||
|
myString string
|
||||||
|
myStruct struct{ field interface{} }
|
||||||
|
myUnsafePointer unsafe.Pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
title string
|
||||||
|
value interface{}
|
||||||
|
expect string
|
||||||
|
}{
|
||||||
|
{"nil", nil, "nil"},
|
||||||
|
{"false", false, "false"},
|
||||||
|
{"true", true, "true"},
|
||||||
|
{"custom false", myBool(false), "false"},
|
||||||
|
{"custom true", myBool(true), "true"},
|
||||||
|
{"int", 42, "42"},
|
||||||
|
{"negative int", -42, "-42"},
|
||||||
|
{"custom int", myInt(42), "42"},
|
||||||
|
{"uint", uint(42), "42"},
|
||||||
|
{"byte", byte(42), "42"},
|
||||||
|
{"round float", float64(42), "42"},
|
||||||
|
{"custom round float", myFloat(42), "42"},
|
||||||
|
{"float with fraction", 1.8, "1.8"},
|
||||||
|
{"custom float with fraction", myFloat(1.8), "1.8"},
|
||||||
|
{"complex", 2 + 3i, "2+3i"},
|
||||||
|
{"custom complex", myComplex(2 + 3i), "2+3i"},
|
||||||
|
{"imaginary", 3i, "0+3i"},
|
||||||
|
{"custom imaginary", myComplex(3i), "0+3i"},
|
||||||
|
{"array", [...]int{1, 2, 3}, "[3]{1, 2, 3}"},
|
||||||
|
{"custom array", myArray{1, 2, 3}, "[3]{1, 2, 3}"},
|
||||||
|
{"channel", make(chan int), "chan"},
|
||||||
|
{"custom channel", make(myChannel), "chan"},
|
||||||
|
{"nil channel", struct{ c chan int }{}, "{c: nil}"},
|
||||||
|
{"nil custom channel", struct{ c myChannel }{}, "{c: nil}"},
|
||||||
|
{"receive channel", struct{ c <-chan int }{make(chan int)}, "{c: chan}"},
|
||||||
|
{"send channel", struct{ c chan<- int }{make(chan int)}, "{c: chan}"},
|
||||||
|
{"function", func() {}, "func()"},
|
||||||
|
{"function with args", func(int, int) int { return 0 }, "func()"},
|
||||||
|
{"custom function with args", myFunction(func(int, int) int { return 0 }), "func()"},
|
||||||
|
{"function with multiple return args", func(int, int) (int, int) { return 0, 0 }, "func()"},
|
||||||
|
{"nil function", struct{ f func(int) int }{}, "{f: nil}"},
|
||||||
|
{"interface type", struct{ i interface{ foo() } }{}, "{i: nil}"},
|
||||||
|
{"map", map[int]int{24: 42}, "map{24: 42}"},
|
||||||
|
{"custom map", myMap{24: 42}, "map{24: 42}"},
|
||||||
|
{"nil map", struct{ m map[int]int }{}, "{m: nil}"},
|
||||||
|
{"nil custom map", struct{ m myMap }{}, "{m: nil}"},
|
||||||
|
{"pointer", &struct{}{}, "{}"},
|
||||||
|
{"custom pointer", &myStruct{}, "{field: nil}"},
|
||||||
|
{"nil pointer", struct{ p *int }{}, "{p: nil}"},
|
||||||
|
{"nil custom pointer", struct{ p myPointer }{}, "{p: nil}"},
|
||||||
|
{"list", []int{1, 2, 3}, "[]{1, 2, 3}"},
|
||||||
|
{"custom list", myList{1, 2, 3}, "[]{1, 2, 3}"},
|
||||||
|
{"nil list", struct{ l []int }{}, "{l: nil}"},
|
||||||
|
{"nil custom list", struct{ l myList }{}, "{l: nil}"},
|
||||||
|
{"string", "\\\"\b\f\n\r\t\vfoo", "\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\""},
|
||||||
|
{"custom string", myString("\\\"\b\f\n\r\t\vfoo"), "\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\""},
|
||||||
|
{"structure", struct{ foo int }{42}, "{foo: 42}"},
|
||||||
|
{"custom structure", myStruct{42}, "{field: 42}"},
|
||||||
|
{"unsafe pointer", unsafe.Pointer(&struct{}{}), "pointer"},
|
||||||
|
{"custom unsafe pointer", myUnsafePointer(&struct{}{}), "pointer"},
|
||||||
|
{"unsafe pointer type", struct{ p unsafe.Pointer }{}, "{p: nil}"},
|
||||||
|
} {
|
||||||
|
t.Run(test.title, func(t *testing.T) {
|
||||||
|
s := Sprint(test.value)
|
||||||
|
if s != test.expect {
|
||||||
|
t.Fatalf("expected: %s, got: %s", test.expect, s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSprintt(t *testing.T) {
|
||||||
|
type (
|
||||||
|
myBool bool
|
||||||
|
myInt int
|
||||||
|
myFloat float64
|
||||||
|
myComplex complex64
|
||||||
|
myArray [3]int
|
||||||
|
myChannel chan int
|
||||||
|
myFunction func(int, int) int
|
||||||
|
myMap map[int]int
|
||||||
|
myPointer *int
|
||||||
|
myList []int
|
||||||
|
myString string
|
||||||
|
myStruct struct{ field interface{} }
|
||||||
|
myUnsafePointer unsafe.Pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
title string
|
||||||
|
value interface{}
|
||||||
|
expect string
|
||||||
|
}{
|
||||||
|
{"nil", nil, "nil"},
|
||||||
|
{"false", false, "false"},
|
||||||
|
{"true", true, "true"},
|
||||||
|
{"custom false", myBool(false), "myBool(false)"},
|
||||||
|
{"custom true", myBool(true), "myBool(true)"},
|
||||||
|
{"int", 42, "42"},
|
||||||
|
{"negative int", -42, "-42"},
|
||||||
|
{"custom int", myInt(42), "myInt(42)"},
|
||||||
|
{"uint", uint(42), "uint(42)"},
|
||||||
|
{"byte", byte(42), "uint8(42)"},
|
||||||
|
{"round float", float64(42), "float64(42)"},
|
||||||
|
{"custom round float", myFloat(42), "myFloat(42)"},
|
||||||
|
{"float with fraction", 1.8, "float64(1.8)"},
|
||||||
|
{"custom float with fraction", myFloat(1.8), "myFloat(1.8)"},
|
||||||
|
{"complex", 2 + 3i, "complex128(2+3i)"},
|
||||||
|
{"custom complex", myComplex(2 + 3i), "myComplex(2+3i)"},
|
||||||
|
{"imaginary", 3i, "complex128(0+3i)"},
|
||||||
|
{"custom imaginary", myComplex(3i), "myComplex(0+3i)"},
|
||||||
|
{"array", [...]int{1, 2, 3}, "[3]int{1, 2, 3}"},
|
||||||
|
{"custom array", myArray{1, 2, 3}, "myArray{1, 2, 3}"},
|
||||||
|
{"channel", make(chan int), "chan int"},
|
||||||
|
{"custom channel", make(myChannel), "myChannel"},
|
||||||
|
{"nil channel", struct{ c chan int }{}, "struct{c chan int}{c: nil}"},
|
||||||
|
{"nil custom channel", struct{ c myChannel }{}, "struct{c myChannel}{c: nil}"},
|
||||||
|
{"receive channel", struct{ c <-chan int }{make(chan int)}, "struct{c <-chan int}{c: chan}"},
|
||||||
|
{"send channel", struct{ c chan<- int }{make(chan int)}, "struct{c chan<- int}{c: chan}"},
|
||||||
|
{"function", func() {}, "func()"},
|
||||||
|
{"function with args", func(int, int) int { return 0 }, "func(int, int) int"},
|
||||||
|
{"custom function with args", myFunction(func(int, int) int { return 0 }), "myFunction"},
|
||||||
|
{"function with multiple return args", func(int, int) (int, int) { return 0, 0 }, "func(int, int) (int, int)"},
|
||||||
|
{"nil function", struct{ f func(int) int }{}, "struct{f func(int) int}{f: nil}"},
|
||||||
|
{"interface type", struct{ i interface{ foo() } }{}, "struct{i interface{foo()}}{i: nil}"},
|
||||||
|
{"map", map[int]int{24: 42}, "map[int]int{24: 42}"},
|
||||||
|
{"custom map", myMap{24: 42}, "myMap{24: 42}"},
|
||||||
|
{"nil map", struct{ m map[int]int }{}, "struct{m map[int]int}{m: nil}"},
|
||||||
|
{"nil custom map", struct{ m myMap }{}, "struct{m myMap}{m: nil}"},
|
||||||
|
{"pointer", &struct{}{}, "*struct{}{}"},
|
||||||
|
{"custom pointer", &myStruct{}, "*myStruct{field: nil}"},
|
||||||
|
{"nil pointer", struct{ p *int }{}, "struct{p *int}{p: nil}"},
|
||||||
|
{"nil custom pointer", struct{ p myPointer }{}, "struct{p myPointer}{p: nil}"},
|
||||||
|
{"list", []int{1, 2, 3}, "[]int{1, 2, 3}"},
|
||||||
|
{"custom list", myList{1, 2, 3}, "myList{1, 2, 3}"},
|
||||||
|
{"nil list", struct{ l []int }{}, "struct{l []int}{l: nil}"},
|
||||||
|
{"nil custom list", struct{ l myList }{}, "struct{l myList}{l: nil}"},
|
||||||
|
{"string", "\\\"\b\f\n\r\t\vfoo", "\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\""},
|
||||||
|
{"custom string", myString("\\\"\b\f\n\r\t\vfoo"), "myString(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")"},
|
||||||
|
{"structure", struct{ foo int }{42}, "struct{foo int}{foo: 42}"},
|
||||||
|
{"custom structure", myStruct{42}, "myStruct{field: 42}"},
|
||||||
|
{"unsafe pointer", unsafe.Pointer(&struct{}{}), "pointer"},
|
||||||
|
{"custom unsafe pointer", myUnsafePointer(&struct{}{}), "pointer"},
|
||||||
|
{"unsafe pointer type", struct{ p unsafe.Pointer }{}, "struct{p Pointer}{p: nil}"},
|
||||||
|
} {
|
||||||
|
t.Run(test.title, func(t *testing.T) {
|
||||||
|
s := Sprintt(test.value)
|
||||||
|
if s != test.expect {
|
||||||
|
t.Fatalf("expected: %s, got: %s", test.expect, s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSprintv(t *testing.T) {
|
||||||
|
type (
|
||||||
|
myBool bool
|
||||||
|
myInt int
|
||||||
|
myFloat float64
|
||||||
|
myComplex complex64
|
||||||
|
myArray [3]int
|
||||||
|
myChannel chan int
|
||||||
|
myFunction func(int, int) int
|
||||||
|
myMap map[int]int
|
||||||
|
myPointer *int
|
||||||
|
myList []int
|
||||||
|
myString string
|
||||||
|
myStruct struct{ field interface{} }
|
||||||
|
myUnsafePointer unsafe.Pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
title string
|
||||||
|
value interface{}
|
||||||
|
expect string
|
||||||
|
}{
|
||||||
|
{"nil", nil, "nil"},
|
||||||
|
{"false", false, "bool(false)"},
|
||||||
|
{"true", true, "bool(true)"},
|
||||||
|
{"custom false", myBool(false), "myBool(false)"},
|
||||||
|
{"custom true", myBool(true), "myBool(true)"},
|
||||||
|
{"int", 42, "int(42)"},
|
||||||
|
{"negative int", -42, "int(-42)"},
|
||||||
|
{"custom int", myInt(42), "myInt(42)"},
|
||||||
|
{"uint", uint(42), "uint(42)"},
|
||||||
|
{"byte", byte(42), "uint8(42)"},
|
||||||
|
{"round float", float64(42), "float64(42)"},
|
||||||
|
{"custom round float", myFloat(42), "myFloat(42)"},
|
||||||
|
{"float with fraction", 1.8, "float64(1.8)"},
|
||||||
|
{"custom float with fraction", myFloat(1.8), "myFloat(1.8)"},
|
||||||
|
{"complex", 2 + 3i, "complex128(2+3i)"},
|
||||||
|
{"custom complex", myComplex(2 + 3i), "myComplex(2+3i)"},
|
||||||
|
{"imaginary", 3i, "complex128(0+3i)"},
|
||||||
|
{"custom imaginary", myComplex(3i), "myComplex(0+3i)"},
|
||||||
|
{"array", [...]int{1, 2, 3}, "[3]int{int(1), int(2), int(3)}"},
|
||||||
|
{"custom array", myArray{1, 2, 3}, "myArray{int(1), int(2), int(3)}"},
|
||||||
|
{"channel", make(chan int), "chan int"},
|
||||||
|
{"custom channel", make(myChannel), "myChannel"},
|
||||||
|
{"nil channel", struct{ c chan int }{}, "struct{c chan int}{c: chan int(nil)}"},
|
||||||
|
{"nil custom channel", struct{ c myChannel }{}, "struct{c myChannel}{c: myChannel(nil)}"},
|
||||||
|
{"receive channel", struct{ c <-chan int }{make(chan int)}, "struct{c <-chan int}{c: <-chan int}"},
|
||||||
|
{"send channel", struct{ c chan<- int }{make(chan int)}, "struct{c chan<- int}{c: chan<- int}"},
|
||||||
|
{"function", func() {}, "func()"},
|
||||||
|
{"function with args", func(int, int) int { return 0 }, "func(int, int) int"},
|
||||||
|
{"custom function with args", myFunction(func(int, int) int { return 0 }), "myFunction"},
|
||||||
|
{"function with multiple return args", func(int, int) (int, int) { return 0, 0 }, "func(int, int) (int, int)"},
|
||||||
|
{"nil function", struct{ f func(int) int }{}, "struct{f func(int) int}{f: func(int) int(nil)}"},
|
||||||
|
{"interface type", struct{ i interface{ foo() } }{}, "struct{i interface{foo()}}{i: interface{foo()}(nil)}"},
|
||||||
|
{"map", map[int]int{24: 42}, "map[int]int{int(24): int(42)}"},
|
||||||
|
{"custom map", myMap{24: 42}, "myMap{int(24): int(42)}"},
|
||||||
|
{"nil map", struct{ m map[int]int }{}, "struct{m map[int]int}{m: map[int]int(nil)}"},
|
||||||
|
{"nil custom map", struct{ m myMap }{}, "struct{m myMap}{m: myMap(nil)}"},
|
||||||
|
{"pointer", &struct{}{}, "*struct{}{}"},
|
||||||
|
{"custom pointer", &myStruct{}, "*myStruct{field: interface{}(nil)}"},
|
||||||
|
{"nil pointer", struct{ p *int }{}, "struct{p *int}{p: *int(nil)}"},
|
||||||
|
{"nil custom pointer", struct{ p myPointer }{}, "struct{p myPointer}{p: myPointer(nil)}"},
|
||||||
|
{"list", []int{1, 2, 3}, "[]int{int(1), int(2), int(3)}"},
|
||||||
|
{"custom list", myList{1, 2, 3}, "myList{int(1), int(2), int(3)}"},
|
||||||
|
{"nil list", struct{ l []int }{}, "struct{l []int}{l: []int(nil)}"},
|
||||||
|
{"nil custom list", struct{ l myList }{}, "struct{l myList}{l: myList(nil)}"},
|
||||||
|
{"string", "\\\"\b\f\n\r\t\vfoo", "string(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")"},
|
||||||
|
{"custom string", myString("\\\"\b\f\n\r\t\vfoo"), "myString(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")"},
|
||||||
|
{"structure", struct{ foo int }{42}, "struct{foo int}{foo: int(42)}"},
|
||||||
|
{"custom structure", myStruct{42}, "myStruct{field: interface{}(int(42))}"},
|
||||||
|
{"custom structure, nil field", myStruct{}, "myStruct{field: interface{}(nil)}"},
|
||||||
|
{"unsafe pointer", unsafe.Pointer(&struct{}{}), "pointer"},
|
||||||
|
{"custom unsafe pointer", myUnsafePointer(&struct{}{}), "pointer"},
|
||||||
|
{"unsafe pointer type", struct{ p unsafe.Pointer }{}, "struct{p Pointer}{p: Pointer(nil)}"},
|
||||||
|
} {
|
||||||
|
t.Run(test.title, func(t *testing.T) {
|
||||||
|
s := Sprintv(test.value)
|
||||||
|
if s != test.expect {
|
||||||
|
t.Fatalf("expected: %s, got: %s", test.expect, s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSprintInvalid(t *testing.T) {
|
||||||
|
s := sprint(none, reflect.Value{})
|
||||||
|
if s != "<invalid>" {
|
||||||
|
t.Fatalf("expected: <invalid>, got: %s", s)
|
||||||
|
}
|
||||||
|
}
|
118
type.go
Normal file
118
type.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package notation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func funcBase(t reflect.Type) string {
|
||||||
|
args := func(num func() int, typ func(int) reflect.Type) []string {
|
||||||
|
t := make([]string, num())
|
||||||
|
for i := 0; i < num(); i++ {
|
||||||
|
t[i] = sprintType(typ(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
in := args(t.NumIn, t.In)
|
||||||
|
out := args(t.NumOut, t.Out)
|
||||||
|
|
||||||
|
var outs string
|
||||||
|
if len(out) == 1 {
|
||||||
|
outs = out[0]
|
||||||
|
} else if len(out) > 1 {
|
||||||
|
outs = fmt.Sprintf("(%s)", strings.Join(out, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
var s string
|
||||||
|
if outs == "" {
|
||||||
|
s = fmt.Sprintf("(%s)", strings.Join(in, ", "))
|
||||||
|
} else {
|
||||||
|
s = fmt.Sprintf("(%s) %s", strings.Join(in, ", "), outs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func arrayType(t reflect.Type) string {
|
||||||
|
return fmt.Sprintf("[%d]%s", t.Len(), sprintType(t.Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func chanType(t reflect.Type) string {
|
||||||
|
var prefix string
|
||||||
|
switch t.ChanDir() {
|
||||||
|
case reflect.RecvDir:
|
||||||
|
prefix = "<-chan"
|
||||||
|
case reflect.SendDir:
|
||||||
|
prefix = "chan<-"
|
||||||
|
default:
|
||||||
|
prefix = "chan"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s %s", prefix, sprintType(t.Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func funcType(t reflect.Type) string {
|
||||||
|
return fmt.Sprintf("func%s", funcBase(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func interfaceType(t reflect.Type) string {
|
||||||
|
var m []string
|
||||||
|
for i := 0; i < t.NumMethod(); i++ {
|
||||||
|
method := t.Method(i)
|
||||||
|
m = append(m, fmt.Sprintf("%s%s", method.Name, funcBase(method.Type)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("interface{%s}", strings.Join(m, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapType(t reflect.Type) string {
|
||||||
|
return fmt.Sprintf("map[%s]%s", sprintType(t.Key()), sprintType(t.Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func pointerType(t reflect.Type) string {
|
||||||
|
return fmt.Sprintf("*%s", sprintType(t.Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func listType(t reflect.Type) string {
|
||||||
|
return fmt.Sprintf("[]%s", sprintType(t.Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func structType(t reflect.Type) string {
|
||||||
|
f := make([]string, t.NumField())
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
fi := t.Field(i)
|
||||||
|
f[i] = fmt.Sprintf("%s %s", fi.Name, sprintType(fi.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("struct{%s}", strings.Join(f, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func sprintType(t reflect.Type) string {
|
||||||
|
if t.Name() != "" {
|
||||||
|
return t.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Array:
|
||||||
|
return arrayType(t)
|
||||||
|
case reflect.Chan:
|
||||||
|
return chanType(t)
|
||||||
|
case reflect.Func:
|
||||||
|
return funcType(t)
|
||||||
|
case reflect.Interface:
|
||||||
|
return interfaceType(t)
|
||||||
|
case reflect.Map:
|
||||||
|
return mapType(t)
|
||||||
|
case reflect.Ptr:
|
||||||
|
return pointerType(t)
|
||||||
|
case reflect.Slice:
|
||||||
|
return listType(t)
|
||||||
|
case reflect.Struct:
|
||||||
|
return structType(t)
|
||||||
|
default:
|
||||||
|
return "<invalid>"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user