ufmt.gno
4.83 Kb · 189 lines
1package ufmt
2
3import (
4 "io"
5 "strings"
6)
7
8// State is what a Formatter's Format method is handed: the flags, width and
9// precision of the verb being printed, and somewhere to write.
10type State interface {
11 // Write emits formatted output.
12 Write(b []byte) (n int, err error)
13 // Width returns the width and whether one was set.
14 Width() (wid int, ok bool)
15 // Precision returns the precision and whether one was set.
16 Precision() (prec int, ok bool)
17 // Flag reports whether the flag c, a character, was set.
18 Flag(c int) bool
19}
20
21// Formatter is implemented by a value that formats itself. Format is called
22// for every verb, and its output replaces what ufmt would have printed.
23type Formatter interface {
24 Format(f State, verb rune)
25}
26
27// Stringer is implemented by a value that has a natural string form. It is
28// used for %v, %s, %q, %x and %X, and by Print.
29type Stringer interface {
30 String() string
31}
32
33// GoStringer is implemented by a value that has a Go-syntax form, used for %#v.
34type GoStringer interface {
35 GoString() string
36}
37
38// Sprintf formats according to a format specifier and returns the string.
39func Sprintf(format string, a ...any) string {
40 p := newPrinter()
41 p.doPrintf(format, a)
42 return string(p.buf)
43}
44
45// Appendf formats according to a format specifier and appends the result to b.
46func Appendf(b []byte, format string, a ...any) []byte {
47 p := newPrinter()
48 p.doPrintf(format, a)
49 return append(b, p.buf...)
50}
51
52// Fprintf formats according to a format specifier and writes to w.
53func Fprintf(w io.Writer, format string, a ...any) (n int, err error) {
54 p := newPrinter()
55 p.doPrintf(format, a)
56 return w.Write(p.buf)
57}
58
59// Printf formats according to a format specifier and prints the result with
60// the print builtin, which is what a test or a filetest captures.
61func Printf(format string, a ...any) (n int, err error) {
62 return printOut(Sprintf(format, a...))
63}
64
65// Sprint formats its operands with %v and returns the string. A space is added
66// between two operands when neither is a string.
67func Sprint(a ...any) string {
68 p := newPrinter()
69 p.doPrint(a)
70 return string(p.buf)
71}
72
73// Append formats its operands as Sprint does and appends the result to b.
74func Append(b []byte, a ...any) []byte {
75 p := newPrinter()
76 p.doPrint(a)
77 return append(b, p.buf...)
78}
79
80// Fprint formats its operands as Sprint does and writes to w.
81func Fprint(w io.Writer, a ...any) (n int, err error) {
82 p := newPrinter()
83 p.doPrint(a)
84 return w.Write(p.buf)
85}
86
87// Print formats its operands as Sprint does and prints the result.
88func Print(a ...any) (n int, err error) {
89 return printOut(Sprint(a...))
90}
91
92// Sprintln formats its operands with %v, always separated by a space, and
93// appends a newline.
94func Sprintln(a ...any) string {
95 p := newPrinter()
96 p.doPrintln(a)
97 return string(p.buf)
98}
99
100// Appendln formats its operands as Sprintln does and appends the result to b.
101func Appendln(b []byte, a ...any) []byte {
102 p := newPrinter()
103 p.doPrintln(a)
104 return append(b, p.buf...)
105}
106
107// Fprintln formats its operands as Sprintln does and writes to w.
108func Fprintln(w io.Writer, a ...any) (n int, err error) {
109 p := newPrinter()
110 p.doPrintln(a)
111 return w.Write(p.buf)
112}
113
114// Println formats its operands as Sprintln does and prints the result.
115func Println(a ...any) (n int, err error) {
116 return printOut(Sprintln(a...))
117}
118
119func printOut(s string) (int, error) {
120 var sb strings.Builder
121 n, err := sb.WriteString(s)
122 print(sb.String())
123 return n, err
124}
125
126// Errorf formats according to a format specifier and returns the string as an
127// error. Each %w operand must be an error; it is printed as %v and returned by
128// the error's Unwrap method, so errors.Is and errors.Unwrap can find it. One %w
129// gives an Unwrap() error, several give an Unwrap() []error, as in Go.
130func Errorf(format string, a ...any) error {
131 p := newPrinter()
132 p.wrapErrs = true
133 p.doPrintf(format, a)
134 s := string(p.buf)
135
136 switch len(p.wrapped) {
137 case 0:
138 return &errorString{s}
139 case 1:
140 w := &wrapError{msg: s}
141 w.err, _ = a[p.wrapped[0]].(error)
142 return w
143 default:
144 if p.reordered {
145 sortInts(p.wrapped)
146 }
147 var errs []error
148 for i, argNum := range p.wrapped {
149 if i > 0 && p.wrapped[i-1] == argNum {
150 continue
151 }
152 if e, ok := a[argNum].(error); ok {
153 errs = append(errs, e)
154 }
155 }
156 return &wrapErrors{s, errs}
157 }
158}
159
160type errorString struct {
161 s string
162}
163
164func (e *errorString) Error() string { return e.s }
165
166type wrapError struct {
167 msg string
168 err error
169}
170
171func (e *wrapError) Error() string { return e.msg }
172func (e *wrapError) Unwrap() error { return e.err }
173
174type wrapErrors struct {
175 msg string
176 errs []error
177}
178
179func (e *wrapErrors) Error() string { return e.msg }
180func (e *wrapErrors) Unwrap() []error { return e.errs }
181
182// sortInts is an insertion sort: the slice holds one entry per %w.
183func sortInts(a []int) {
184 for i := 1; i < len(a); i++ {
185 for j := i; j > 0 && a[j] < a[j-1]; j-- {
186 a[j], a[j-1] = a[j-1], a[j]
187 }
188 }
189}