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