print.gno
19.39 Kb · 894 lines
1package ufmt
2
3import (
4 "sort"
5 "unicode/utf8"
6)
7
8// This file is the high level: parsing the format, and choosing how each
9// operand prints. It is a port of print.go in Go's fmt, minus reflect.
10
11const (
12 commaSpace = ", "
13 nilAngle = "<nil>"
14 nilParen = "(nil)"
15 unhandled = "(unhandled)"
16 percentBang = "%!"
17 missing = "(MISSING)"
18 badIndex = "(BADINDEX)"
19 extra = "%!(EXTRA "
20 badWidth = "%!(BADWIDTH)"
21 badPrec = "%!(BADPREC)"
22 noVerb = "%!(NOVERB)"
23)
24
25// printer holds the output and the state of the verb being printed.
26type printer struct {
27 buf []byte
28
29 // The flags, width and precision of the current verb.
30 plus, minus, sharp, space, zero bool
31 plusV, sharpV bool // %+v and %#v
32 wid, prec int
33 widPresent, precPresent bool
34
35 arg any // the operand being printed, for error messages
36 erroring bool // printing an error message: do not call methods
37 reordered bool // an explicit index was used
38 goodArgNum bool // the last index was valid
39 wrapErrs bool // Errorf: %w is allowed
40 wrapped []int
41}
42
43func newPrinter() *printer {
44 return &printer{}
45}
46
47// Write implements State, so a Formatter can write.
48func (p *printer) Write(b []byte) (int, error) {
49 p.buf = append(p.buf, b...)
50 return len(b), nil
51}
52
53// WriteString lets a Formatter write a string without a conversion.
54func (p *printer) WriteString(s string) (int, error) {
55 p.buf = append(p.buf, s...)
56 return len(s), nil
57}
58
59// Width implements State.
60func (p *printer) Width() (int, bool) { return p.wid, p.widPresent }
61
62// Precision implements State.
63func (p *printer) Precision() (int, bool) { return p.prec, p.precPresent }
64
65// Flag implements State.
66func (p *printer) Flag(c int) bool {
67 switch c {
68 case '-':
69 return p.minus
70 case '+':
71 return p.plus || p.plusV
72 case '#':
73 return p.sharp || p.sharpV
74 case ' ':
75 return p.space
76 case '0':
77 return p.zero
78 }
79 return false
80}
81
82func (p *printer) writeString(s string) { p.buf = append(p.buf, s...) }
83func (p *printer) writeByte(c byte) { p.buf = append(p.buf, c) }
84func (p *printer) writeRune(r rune) { p.buf = utf8.AppendRune(p.buf, r) }
85
86// doPrint is Sprint: %v, with a space between two operands neither of which is
87// a string.
88func (p *printer) doPrint(a []any) {
89 prevString := false
90 for argNum, arg := range a {
91 _, isString := arg.(string)
92 if argNum > 0 && !isString && !prevString {
93 p.writeByte(' ')
94 }
95 p.printArg(arg, 'v')
96 prevString = isString
97 }
98}
99
100// doPrintln is Sprintln: %v, always a space between operands, then a newline.
101func (p *printer) doPrintln(a []any) {
102 for argNum, arg := range a {
103 if argNum > 0 {
104 p.writeByte(' ')
105 }
106 p.printArg(arg, 'v')
107 }
108 p.writeByte('\n')
109}
110
111// parsenum reads a decimal number at s[start:end].
112func parsenum(s string, start, end int) (num int, isnum bool, newi int) {
113 if start >= end {
114 return 0, false, end
115 }
116 for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
117 if num > 1e6 {
118 return 0, false, end // absurdly large: refuse rather than allocate
119 }
120 num = num*10 + int(s[newi]-'0')
121 isnum = true
122 }
123 return
124}
125
126// intFromArg reads a * width or precision from a[argNum].
127func intFromArg(a []any, argNum int) (num int, isInt bool, newArgNum int) {
128 newArgNum = argNum
129 if argNum >= len(a) {
130 return
131 }
132 isInt = true
133 switch v := a[argNum].(type) {
134 case int:
135 num = v
136 case int8:
137 num = int(v)
138 case int16:
139 num = int(v)
140 case int32:
141 num = int(v)
142 case int64:
143 num = int(v)
144 case uint:
145 num = int(v)
146 case uint8:
147 num = int(v)
148 case uint16:
149 num = int(v)
150 case uint32:
151 num = int(v)
152 case uint64:
153 num = int(v)
154 default:
155 isInt = false
156 }
157 newArgNum = argNum + 1
158 if num > 1e6 || num < -1e6 {
159 num = 0
160 isInt = false
161 }
162 return
163}
164
165// argNumber reads an explicit index, "[3]", at format[i:].
166func (p *printer) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) {
167 if len(format) <= i || format[i] != '[' {
168 return argNum, i, false
169 }
170 p.reordered = true
171 for j := 1; j < len(format)-i; j++ {
172 if format[i+j] == ']' {
173 width, ok, newi := parsenum(format, i+1, i+j)
174 if !ok || newi != i+j {
175 p.goodArgNum = false
176 return argNum, i + j + 1, true
177 }
178 index := width - 1
179 if index >= 0 && index < numArgs {
180 return index, i + j + 1, true
181 }
182 p.goodArgNum = false
183 return argNum, i + j + 1, true
184 }
185 }
186 p.goodArgNum = false
187 return argNum, i + 1, false
188}
189
190// doPrintf is Sprintf.
191func (p *printer) doPrintf(format string, a []any) {
192 end := len(format)
193 argNum := 0
194 afterIndex := false // the previous item was an index like [3]
195 p.reordered = false
196
197 for i := 0; i < end; {
198 p.goodArgNum = true
199 lasti := i
200 for i < end && format[i] != '%' {
201 i++
202 }
203 if i > lasti {
204 p.writeString(format[lasti:i])
205 }
206 if i >= end {
207 break
208 }
209 i++ // skip '%'
210
211 p.clearFlags()
212
213 // Fast path: a bare lowercase verb, "%d" or "%s", is most of what any
214 // realm prints, and skips the specifier parsing entirely.
215 if i < end && 'a' <= format[i] && format[i] <= 'z' && argNum < len(a) {
216 verb := rune(format[i])
217 if verb == 'w' {
218 p.wrapped = append(p.wrapped, argNum)
219 }
220 p.printArg(a[argNum], verb)
221 argNum++
222 i++
223 continue
224 }
225
226 flags:
227 for ; i < end; i++ {
228 switch format[i] {
229 case '#':
230 p.sharp = true
231 case '0':
232 p.zero = !p.minus // zero padding only ever goes on the left
233 case '+':
234 p.plus = true
235 case '-':
236 p.minus = true
237 p.zero = false
238 case ' ':
239 p.space = true
240 default:
241 break flags
242 }
243 }
244
245 argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
246
247 // Width.
248 if i < end && format[i] == '*' {
249 i++
250 p.wid, p.widPresent, argNum = intFromArg(a, argNum)
251 if !p.widPresent {
252 p.writeString(badWidth)
253 }
254 if p.wid < 0 {
255 p.wid = -p.wid
256 p.minus = true
257 p.zero = false
258 }
259 afterIndex = false
260 } else {
261 p.wid, p.widPresent, i = parsenum(format, i, end)
262 if afterIndex && p.widPresent { // "%[3]2d"
263 p.goodArgNum = false
264 }
265 }
266
267 // Precision.
268 if i+1 < end && format[i] == '.' {
269 i++
270 if afterIndex { // "%[3].2d"
271 p.goodArgNum = false
272 }
273 argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
274 if i < end && format[i] == '*' {
275 i++
276 p.prec, p.precPresent, argNum = intFromArg(a, argNum)
277 if p.prec < 0 {
278 p.prec = 0
279 p.precPresent = false
280 }
281 if !p.precPresent {
282 p.writeString(badPrec)
283 }
284 afterIndex = false
285 } else {
286 p.prec, p.precPresent, i = parsenum(format, i, end)
287 if !p.precPresent {
288 p.prec = 0
289 p.precPresent = true
290 }
291 }
292 }
293
294 if !afterIndex {
295 argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
296 }
297
298 if i >= end {
299 p.writeString(noVerb)
300 break
301 }
302
303 verb, size := rune(format[i]), 1
304 if verb >= utf8.RuneSelf {
305 verb, size = utf8.DecodeRuneInString(format[i:])
306 }
307 i += size
308
309 switch {
310 case verb == '%': // takes no operand, ignores width and precision
311 p.writeByte('%')
312 case !p.goodArgNum:
313 p.badArgNum(verb)
314 case argNum >= len(a):
315 p.missingArg(verb)
316 default:
317 if verb == 'w' {
318 p.wrapped = append(p.wrapped, argNum)
319 }
320 if verb == 'v' || verb == 'w' {
321 p.sharpV, p.sharp = p.sharp, false
322 p.plusV, p.plus = p.plus, false
323 }
324 p.printArg(a[argNum], verb)
325 argNum++
326 }
327 }
328
329 // Report unused operands, unless an index made the count meaningless.
330 if !p.reordered && argNum < len(a) {
331 p.clearFlags()
332 p.writeString(extra)
333 for i, arg := range a[argNum:] {
334 if i > 0 {
335 p.writeString(commaSpace)
336 }
337 if arg == nil {
338 p.writeString(nilAngle)
339 continue
340 }
341 p.writeString(typeName(arg))
342 p.writeByte('=')
343 p.printArg(arg, 'v')
344 }
345 p.writeByte(')')
346 }
347}
348
349func (p *printer) badArgNum(verb rune) {
350 p.writeString(percentBang)
351 p.writeRune(verb)
352 p.writeString(badIndex)
353}
354
355func (p *printer) missingArg(verb rune) {
356 p.writeString(percentBang)
357 p.writeRune(verb)
358 p.writeString(missing)
359}
360
361// badVerb writes "%!d(string=hi)": the verb, the operand's type and its value.
362func (p *printer) badVerb(verb rune) {
363 p.erroring = true
364 p.writeString(percentBang)
365 p.writeRune(verb)
366 p.writeByte('(')
367 switch {
368 case p.arg == nil:
369 p.writeString(nilAngle)
370 default:
371 if t := typeName(p.arg); t != "unknown" {
372 p.writeString(t)
373 p.writeByte('=')
374 p.printArg(p.arg, 'v')
375 } else {
376 p.writeString("unhandled")
377 }
378 }
379 p.writeByte(')')
380 p.erroring = false
381}
382
383// printArg prints one operand.
384func (p *printer) printArg(arg any, verb rune) {
385 p.arg = arg
386
387 if arg == nil {
388 switch verb {
389 case 'T', 'v':
390 p.padString(nilAngle)
391 default:
392 p.badVerb(verb)
393 }
394 return
395 }
396
397 switch verb {
398 case 'T':
399 p.fmtS(typeName(arg))
400 return
401 case 'p':
402 p.badVerb(verb)
403 return
404 }
405
406 // The basic types first, exactly as Go: a named type never matches these,
407 // so its methods are still found below.
408 switch f := arg.(type) {
409 case bool:
410 p.fmtBool(f, verb)
411 case float32:
412 p.fmtFloatVerb(float64(f), 32, verb)
413 case float64:
414 p.fmtFloatVerb(f, 64, verb)
415 case int:
416 p.fmtSigned(int64(f), verb)
417 case int8:
418 p.fmtSigned(int64(f), verb)
419 case int16:
420 p.fmtSigned(int64(f), verb)
421 case int32:
422 p.fmtSigned(int64(f), verb)
423 case int64:
424 p.fmtSigned(f, verb)
425 case uint:
426 p.fmtUnsigned(uint64(f), verb)
427 case uint8:
428 p.fmtUnsigned(uint64(f), verb)
429 case uint16:
430 p.fmtUnsigned(uint64(f), verb)
431 case uint32:
432 p.fmtUnsigned(uint64(f), verb)
433 case uint64:
434 p.fmtUnsigned(f, verb)
435 case string:
436 p.fmtString(f, verb)
437 case []byte:
438 p.fmtBytes(f, verb)
439 default:
440 if !p.handleMethods(verb) {
441 p.printComposite(arg, verb)
442 }
443 }
444}
445
446// handleMethods prints arg with its own Format, GoString, Error or String
447// method, in Go's order, and reports whether it did.
448func (p *printer) handleMethods(verb rune) (handled bool) {
449 if p.erroring {
450 return false
451 }
452 if verb == 'w' {
453 // %w is for Errorf, and for an error.
454 if _, ok := p.arg.(error); !ok || !p.wrapErrs {
455 p.badVerb(verb)
456 return true
457 }
458 verb = 'v'
459 }
460
461 if f, ok := p.arg.(Formatter); ok {
462 handled = true // stays true if the method panics
463 defer p.catchPanic(p.arg, verb, "Format")
464 f.Format(p, verb)
465 return
466 }
467
468 if p.sharpV {
469 if g, ok := p.arg.(GoStringer); ok {
470 handled = true
471 defer p.catchPanic(p.arg, verb, "GoString")
472 p.fmtS(g.GoString())
473 return
474 }
475 return false
476 }
477
478 switch verb {
479 case 'v', 's', 'x', 'X', 'q':
480 switch v := p.arg.(type) {
481 case error:
482 handled = true
483 defer p.catchPanic(p.arg, verb, "Error")
484 p.fmtString(v.Error(), verb)
485 return
486 case Stringer:
487 handled = true
488 defer p.catchPanic(p.arg, verb, "String")
489 p.fmtString(v.String(), verb)
490 return
491 }
492 }
493 return false
494}
495
496// catchPanic turns a panicking method into "%!v(PANIC=String method: msg)".
497func (p *printer) catchPanic(arg any, verb rune, method string) {
498 r := recover()
499 if r == nil {
500 return
501 }
502 if p.erroring {
503 panic(r) // the method panicked again while printing its own panic
504 }
505 p.clearFlags()
506 p.writeString(percentBang)
507 p.writeRune(verb)
508 p.writeString("(PANIC=")
509 p.writeString(method)
510 p.writeString(" method: ")
511 p.erroring = true
512 p.printArg(r, 'v')
513 p.erroring = false
514 p.writeByte(')')
515}
516
517func (p *printer) fmtBool(v bool, verb rune) {
518 switch verb {
519 case 't', 'v':
520 p.fmtBoolean(v)
521 default:
522 p.badVerb(verb)
523 }
524}
525
526// fmtSigned prints a signed integer. The magnitude of math.MinInt64 does not
527// fit an int64, so it is computed without ever negating v itself.
528func (p *printer) fmtSigned(v int64, verb rune) {
529 if v >= 0 {
530 p.fmtIntegerVerb(uint64(v), false, true, verb)
531 return
532 }
533 mag := uint64(-(v + 1)) + 1
534 p.fmtIntegerVerb(mag, true, true, verb)
535}
536
537func (p *printer) fmtUnsigned(u uint64, verb rune) {
538 p.fmtIntegerVerb(u, false, false, verb)
539}
540
541func (p *printer) fmtIntegerVerb(u uint64, negative, isSigned bool, verb rune) {
542 switch verb {
543 case 'v':
544 if p.sharpV && !isSigned {
545 p.fmt0x64(u, true)
546 } else {
547 p.fmtInteger(u, negative, 10, verb, ldigits)
548 }
549 case 'd':
550 p.fmtInteger(u, negative, 10, verb, ldigits)
551 case 'b':
552 p.fmtInteger(u, negative, 2, verb, ldigits)
553 case 'o', 'O':
554 p.fmtInteger(u, negative, 8, verb, ldigits)
555 case 'x':
556 p.fmtInteger(u, negative, 16, verb, ldigits)
557 case 'X':
558 p.fmtInteger(u, negative, 16, verb, udigits)
559 case 'c', 'q', 'U':
560 if negative {
561 u = ^u + 1 // back to two's complement, as Go sees it: never a rune
562 }
563 switch verb {
564 case 'c':
565 p.fmtC(u)
566 case 'q':
567 p.fmtQc(u)
568 default:
569 p.fmtUnicode(u)
570 }
571 default:
572 p.badVerb(verb)
573 }
574}
575
576// fmt0x64 prints u in hex with a 0x prefix (leading0x) or without one.
577func (p *printer) fmt0x64(u uint64, leading0x bool) {
578 sharp := p.sharp
579 p.sharp = leading0x
580 p.fmtInteger(u, false, 16, 'v', ldigits)
581 p.sharp = sharp
582}
583
584func (p *printer) fmtFloatVerb(v float64, size int, verb rune) {
585 switch verb {
586 case 'v':
587 p.fmtFloat(v, size, 'g', -1)
588 case 'b', 'g', 'G', 'x', 'X':
589 p.fmtFloat(v, size, verb, -1)
590 case 'f', 'e', 'E':
591 p.fmtFloat(v, size, verb, 6)
592 case 'F':
593 p.fmtFloat(v, size, 'f', 6)
594 default:
595 p.badVerb(verb)
596 }
597}
598
599func (p *printer) fmtString(v string, verb rune) {
600 switch verb {
601 case 'v':
602 if p.sharpV {
603 p.fmtQ(v)
604 } else {
605 p.fmtS(v)
606 }
607 case 's':
608 p.fmtS(v)
609 case 'x':
610 p.fmtSx(v, ldigits)
611 case 'X':
612 p.fmtSx(v, udigits)
613 case 'q':
614 p.fmtQ(v)
615 default:
616 p.badVerb(verb)
617 }
618}
619
620func (p *printer) fmtBytes(v []byte, verb rune) {
621 switch verb {
622 case 'v', 'd':
623 if p.sharpV {
624 p.writeString("[]byte")
625 if v == nil {
626 p.writeString(nilParen)
627 return
628 }
629 p.writeByte('{')
630 for i, c := range v {
631 if i > 0 {
632 p.writeString(commaSpace)
633 }
634 p.fmt0x64(uint64(c), true)
635 }
636 p.writeByte('}')
637 return
638 }
639 p.writeByte('[')
640 for i, c := range v {
641 if i > 0 {
642 p.writeByte(' ')
643 }
644 p.fmtInteger(uint64(c), false, 10, verb, ldigits)
645 }
646 p.writeByte(']')
647 case 's':
648 p.fmtS(string(v))
649 case 'x':
650 p.fmtSx(string(v), ldigits)
651 case 'X':
652 p.fmtSx(string(v), udigits)
653 case 'q':
654 p.fmtQ(string(v))
655 default:
656 // Every other verb applies to each byte, as for any slice.
657 p.writeByte('[')
658 for i, c := range v {
659 if i > 0 {
660 p.writeByte(' ')
661 }
662 p.fmtUnsigned(uint64(c), verb)
663 }
664 p.writeByte(']')
665 }
666}
667
668// printComposite prints the slices and maps gno can see without reflect.
669func (p *printer) printComposite(arg any, verb rune) {
670 switch v := arg.(type) {
671 case []any:
672 p.printSlice("[]interface {}", v == nil, len(v), func(i int) any { return v[i] }, verb)
673 case []string:
674 p.printSlice("[]string", v == nil, len(v), func(i int) any { return v[i] }, verb)
675 case []error:
676 p.printSlice("[]error", v == nil, len(v), func(i int) any { return v[i] }, verb)
677 case []bool:
678 p.printSlice("[]bool", v == nil, len(v), func(i int) any { return v[i] }, verb)
679 case []int:
680 p.printSlice("[]int", v == nil, len(v), func(i int) any { return v[i] }, verb)
681 case []int8:
682 p.printSlice("[]int8", v == nil, len(v), func(i int) any { return v[i] }, verb)
683 case []int16:
684 p.printSlice("[]int16", v == nil, len(v), func(i int) any { return v[i] }, verb)
685 case []int32:
686 p.printSlice("[]int32", v == nil, len(v), func(i int) any { return v[i] }, verb)
687 case []int64:
688 p.printSlice("[]int64", v == nil, len(v), func(i int) any { return v[i] }, verb)
689 case []uint:
690 p.printSlice("[]uint", v == nil, len(v), func(i int) any { return v[i] }, verb)
691 case []uint16:
692 p.printSlice("[]uint16", v == nil, len(v), func(i int) any { return v[i] }, verb)
693 case []uint32:
694 p.printSlice("[]uint32", v == nil, len(v), func(i int) any { return v[i] }, verb)
695 case []uint64:
696 p.printSlice("[]uint64", v == nil, len(v), func(i int) any { return v[i] }, verb)
697 case []float32:
698 p.printSlice("[]float32", v == nil, len(v), func(i int) any { return v[i] }, verb)
699 case []float64:
700 p.printSlice("[]float64", v == nil, len(v), func(i int) any { return v[i] }, verb)
701 case map[string]string:
702 keys := sortedKeys(len(v), func(add func(string)) {
703 for k := range v {
704 add(k)
705 }
706 })
707 p.printMap("map[string]string", v == nil, keys, func(k string) any { return v[k] }, verb)
708 case map[string]any:
709 keys := sortedKeys(len(v), func(add func(string)) {
710 for k := range v {
711 add(k)
712 }
713 })
714 p.printMap("map[string]interface {}", v == nil, keys, func(k string) any { return v[k] }, verb)
715 case map[string]int:
716 keys := sortedKeys(len(v), func(add func(string)) {
717 for k := range v {
718 add(k)
719 }
720 })
721 p.printMap("map[string]int", v == nil, keys, func(k string) any { return v[k] }, verb)
722 case map[string]bool:
723 keys := sortedKeys(len(v), func(add func(string)) {
724 for k := range v {
725 add(k)
726 }
727 })
728 p.printMap("map[string]bool", v == nil, keys, func(k string) any { return v[k] }, verb)
729 default:
730 if verb == 'v' {
731 p.padString(unhandled)
732 } else {
733 p.badVerb(verb)
734 }
735 }
736}
737
738// printSlice prints "[a b]", or "[]T{a, b}" for %#v, each element with verb.
739func (p *printer) printSlice(typ string, isNil bool, n int, at func(int) any, verb rune) {
740 if p.sharpV {
741 p.writeString(typ)
742 if isNil {
743 p.writeString(nilParen)
744 return
745 }
746 p.writeByte('{')
747 for i := 0; i < n; i++ {
748 if i > 0 {
749 p.writeString(commaSpace)
750 }
751 p.printElem(at(i), verb)
752 }
753 p.writeByte('}')
754 return
755 }
756 p.writeByte('[')
757 for i := 0; i < n; i++ {
758 if i > 0 {
759 p.writeByte(' ')
760 }
761 p.printElem(at(i), verb)
762 }
763 p.writeByte(']')
764}
765
766// printMap prints "map[a:1 b:2]", or "map[string]int{"a":1, "b":2}" for %#v,
767// keys sorted as Go sorts them.
768func (p *printer) printMap(typ string, isNil bool, keys []string, at func(string) any, verb rune) {
769 if p.sharpV {
770 p.writeString(typ)
771 if isNil {
772 p.writeString(nilParen)
773 return
774 }
775 p.writeByte('{')
776 } else {
777 p.writeString("map[")
778 }
779 for i, k := range keys {
780 if i > 0 {
781 if p.sharpV {
782 p.writeString(commaSpace)
783 } else {
784 p.writeByte(' ')
785 }
786 }
787 p.printElem(k, verb)
788 p.writeByte(':')
789 p.printElem(at(k), verb)
790 }
791 if p.sharpV {
792 p.writeByte('}')
793 } else {
794 p.writeByte(']')
795 }
796}
797
798// printElem prints one element of a composite, keeping the outer operand for
799// error messages. A nil element prints "<nil>" whatever the verb, as in Go.
800func (p *printer) printElem(v any, verb rune) {
801 outer := p.arg
802 if v == nil {
803 if p.sharpV {
804 p.writeString("interface {}(nil)")
805 } else {
806 p.padString(nilAngle)
807 }
808 } else {
809 p.printArg(v, verb)
810 }
811 p.arg = outer
812}
813
814func sortedKeys(n int, each func(add func(string))) []string {
815 keys := make([]string, 0, n)
816 each(func(k string) { keys = append(keys, k) })
817 sort.Strings(keys)
818 return keys
819}
820
821// typeName is %T for the types gno can name without reflect.
822func typeName(v any) string {
823 switch v.(type) {
824 case bool:
825 return "bool"
826 case int:
827 return "int"
828 case int8:
829 return "int8"
830 case int16:
831 return "int16"
832 case int32:
833 return "int32"
834 case int64:
835 return "int64"
836 case uint:
837 return "uint"
838 case uint8:
839 return "uint8"
840 case uint16:
841 return "uint16"
842 case uint32:
843 return "uint32"
844 case uint64:
845 return "uint64"
846 case float32:
847 return "float32"
848 case float64:
849 return "float64"
850 case string:
851 return "string"
852 case []byte:
853 return "[]uint8"
854 case []any:
855 return "[]interface {}"
856 case []string:
857 return "[]string"
858 case []error:
859 return "[]error"
860 case []bool:
861 return "[]bool"
862 case []int:
863 return "[]int"
864 case []int8:
865 return "[]int8"
866 case []int16:
867 return "[]int16"
868 case []int32:
869 return "[]int32"
870 case []int64:
871 return "[]int64"
872 case []uint:
873 return "[]uint"
874 case []uint16:
875 return "[]uint16"
876 case []uint32:
877 return "[]uint32"
878 case []uint64:
879 return "[]uint64"
880 case []float32:
881 return "[]float32"
882 case []float64:
883 return "[]float64"
884 case map[string]string:
885 return "map[string]string"
886 case map[string]any:
887 return "map[string]interface {}"
888 case map[string]int:
889 return "map[string]int"
890 case map[string]bool:
891 return "map[string]bool"
892 }
893 return "unknown"
894}