mirror of
https://github.com/golang/go
synced 2024-10-02 22:25:08 +00:00
fmt: add a function to recover the original format string given a State
Sometimes when implementing a Formatter it's helpful to use the fmt package without invoking the formatter. This new function, FormatString, makes that easier in some cases by recreating the original formatting directive (such as "%3.2f") that caused Formatter.Format to be called. The original Formatter interface is probably not what we would design today, but we're stuck with it. FormatString, although it takes a State as an argument, compensates by making Formatter a little more flexible. The State does not include the verb so (unlike in the issue), we must provide it explicitly in the call to FormatString. Doing it there minimizes allocations by returning the complete format string. Fixes #51668 Updates #51195 Change-Id: Ie31c8256515864b2f460df45fbd231286b8b7a28 Reviewed-on: https://go-review.googlesource.com/c/go/+/400875 Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Run-TryBot: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
27f1246b85
commit
d75e186e2c
1
api/next/51668.txt
Normal file
1
api/next/51668.txt
Normal file
|
@ -0,0 +1 @@
|
|||
pkg fmt, func FormatString(State, int32) string #51668
|
|
@ -9,6 +9,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
@ -71,6 +72,31 @@ type GoStringer interface {
|
|||
GoString() string
|
||||
}
|
||||
|
||||
// FormatString returns a string representing the fully qualified formatting
|
||||
// directive captured by the State, followed by the argument verb. (State does not
|
||||
// itself contain the verb.) The result has a leading percent sign followed by any
|
||||
// flags, the width, and the precision. Missing flags, width, and precision are
|
||||
// omitted. This function allows a Formatter to reconstruct the original
|
||||
// directive triggering the call to Format.
|
||||
func FormatString(state State, verb rune) string {
|
||||
var tmp [16]byte // Use a local buffer.
|
||||
b := append(tmp[:0], '%')
|
||||
for _, c := range " +-#0" { // All known flags
|
||||
if state.Flag(int(c)) { // The argument is an int for historical reasons.
|
||||
b = append(b, byte(c))
|
||||
}
|
||||
}
|
||||
if w, ok := state.Width(); ok {
|
||||
b = strconv.AppendInt(b, int64(w), 10)
|
||||
}
|
||||
if p, ok := state.Precision(); ok {
|
||||
b = append(b, '.')
|
||||
b = strconv.AppendInt(b, int64(p), 10)
|
||||
}
|
||||
b = utf8.AppendRune(b, verb)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Use simple []byte instead of bytes.Buffer to avoid large dependency.
|
||||
type buffer []byte
|
||||
|
||||
|
|
80
src/fmt/state_test.go
Normal file
80
src/fmt/state_test.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fmt_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testState struct {
|
||||
width int
|
||||
widthOK bool
|
||||
prec int
|
||||
precOK bool
|
||||
flag map[int]bool
|
||||
}
|
||||
|
||||
var _ fmt.State = testState{}
|
||||
|
||||
func (s testState) Write(b []byte) (n int, err error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (s testState) Width() (wid int, ok bool) {
|
||||
return s.width, s.widthOK
|
||||
}
|
||||
|
||||
func (s testState) Precision() (prec int, ok bool) {
|
||||
return s.prec, s.precOK
|
||||
}
|
||||
|
||||
func (s testState) Flag(c int) bool {
|
||||
return s.flag[c]
|
||||
}
|
||||
|
||||
const NO = -1000
|
||||
|
||||
func mkState(w, p int, flags string) testState {
|
||||
s := testState{}
|
||||
if w != NO {
|
||||
s.width = w
|
||||
s.widthOK = true
|
||||
}
|
||||
if p != NO {
|
||||
s.prec = p
|
||||
s.precOK = true
|
||||
}
|
||||
s.flag = make(map[int]bool)
|
||||
for _, c := range flags {
|
||||
s.flag[int(c)] = true
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func TestFormatString(t *testing.T) {
|
||||
var tests = []struct {
|
||||
width, prec int
|
||||
flags string
|
||||
result string
|
||||
}{
|
||||
{NO, NO, "", "%x"},
|
||||
{NO, 3, "", "%.3x"},
|
||||
{3, NO, "", "%3x"},
|
||||
{7, 3, "", "%7.3x"},
|
||||
{NO, NO, " +-#0", "% +-#0x"},
|
||||
{7, 3, "+", "%+7.3x"},
|
||||
{7, -3, "-", "%-7.-3x"},
|
||||
{7, 3, " ", "% 7.3x"},
|
||||
{7, 3, "#", "%#7.3x"},
|
||||
{7, 3, "0", "%07.3x"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
got := fmt.FormatString(mkState(test.width, test.prec, test.flags), 'x')
|
||||
if got != test.result {
|
||||
t.Errorf("%v: got %s", test, got)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue