-
Notifications
You must be signed in to change notification settings - Fork 0
/
writer.go
128 lines (115 loc) · 3.54 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import (
"fmt"
"io"
"strings"
)
// IndentWriter is a wrapper around an io.Writer that will consistently indent
// output and providing methods to simplify the writing of text output
// containing indented sections and the handling of errors.
//
// The IndentWriter is not thread-safe and should not be used concurrently.
//
// If the underlying writer returns an error, the error is stored in the
// IndentWriter and no further output is written by any IndentWriter methods.
type IndentWriter struct {
output io.Writer // the underlying writer
indent string // the current indent string
indented bool // indicates if the indent has been written on the current line
error // any error that occurred during writing
}
// writeIndent writes the current indent string to the output writer (if not
// already written and not in an error state) and clears the indented flag.
func (w *IndentWriter) writeIndent() {
if w.indented || w.error != nil {
return
}
_, w.error = io.WriteString(w.output, w.indent)
w.indented = w.error == nil
}
// WriteIndented calls the specified function with the indent string increased
// by two spaces. The indent is restored to its original value after the
// function returns.
func (w *IndentWriter) WriteIndented(fn func()) {
og := w.indent
defer func() { w.indent = og }()
w.indent += " "
fn()
}
// Write writes the specified arguments to the output writer. If the writer is
// in an error state, no output is written.
//
// One or more arguments may be provided. If more than one argument is provided,
// the first argument is treated as a string to be used as a format string with
// the remaining arguments supplied as values.
//
// If not yet written, the current indent string is written to the output
// writer before the specified arguments.
//
// No newline is appended to the output.
func (w *IndentWriter) Write(s string, args ...any) {
if w.error != nil {
return
}
if len(args) > 0 {
s = fmt.Sprintf(s, args...)
}
w.writeIndent()
_, w.error = io.WriteString(w.output, s)
}
// WriteLn writes the specified arguments with a newline appended.
// Different numbers of arguments are treated differently:
//
// no arguments only a newline is written.
//
// one argument the argument is written and a new-line appended.
//
// multiple arguments the first argument must be a string
// to be used as a format string with the
// remaining arguments supplied as values.
//
// If the writer is in an error state, no output is written.
//
// If not yet written, the current indent string is written to the output
// writer before the specified arguments.
func (w *IndentWriter) WriteLn(args ...any) {
defer func() { w.indented = false }()
var (
s string
a []any
)
switch len(args) {
case 0:
w.Write("\n")
return
case 1:
s = fmt.Sprintf("%s", args[0])
default:
s = args[0].(string)
a = args[1:]
}
if len(a) > 0 {
s = fmt.Sprintf(s, a...)
}
lines := strings.Split(s, "\n")
for _, line := range lines {
w.writeIndent()
if len(line) > 0 {
w.Write(line)
}
w.Write("\n")
}
}
// WriteXMLElement writes an XML element with optional attributes. The content
// is written by calling the specified function with the indent increased by
// two spaces. The element is closed with the appropriate end tag.
func (w *IndentWriter) WriteXMLElement(fn func(), el string, attrs ...string) {
w.Write("<%s", el)
for _, attr := range attrs {
w.Write(" ")
w.Write(attr)
}
w.WriteLn(">")
w.WriteIndented(fn)
w.WriteLn("</%s>", el)
}