-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor report package to be more compatible
* Add additional replacer for ('\', 'n') -> "\n" * New type Formatter embeds Template and writer/tabwriter handling * tabwriter.Init() is exposed to allow updating the tabwriter settings Note: If template origin is OriginPodman or has "table" keyword prefix output will be filtered through tabwriter. Otherwise, output will be rendered using given writer. Note: Once all podman commands have been updated a follow on PR will remove the old report.Template and report.Writer code. See containers/podman#10974 Signed-off-by: Jhon Honce <[email protected]>
- Loading branch information
Showing
5 changed files
with
410 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package report | ||
|
||
import ( | ||
"io" | ||
"strings" | ||
"text/tabwriter" | ||
"text/template" | ||
) | ||
|
||
// Flusher is the interface that wraps the Flush method. | ||
type Flusher interface { | ||
Flush() error | ||
} | ||
|
||
// NopFlusher represents a type which flush operation is nop. | ||
type NopFlusher struct{} | ||
|
||
// Flush is a nop operation. | ||
func (f *NopFlusher) Flush() (err error) { return } | ||
|
||
type Origin int | ||
|
||
const ( | ||
OriginUnknown Origin = iota | ||
OriginPodman | ||
OriginUser | ||
) | ||
|
||
func (o Origin) String() string { | ||
switch o { | ||
case OriginPodman: | ||
return "OriginPodman" | ||
case OriginUser: | ||
return "OriginUser" | ||
default: | ||
return "OriginUnknown" | ||
} | ||
} | ||
|
||
// Formatter holds the configured Writer and parsed Template, additional state fields are | ||
// maintained to assist in the podman command report writing. | ||
type Formatter struct { | ||
Origin Origin // Source of go template. OriginUser or OriginPodman | ||
RenderHeaders bool // Hint, default behavior for given template is to include headers | ||
RenderTable bool // Does template have "table" keyword | ||
flusher Flusher // Flush any buffered formatted output | ||
template *template.Template // Go text/template for formatting output | ||
text string // value of canonical template after processing | ||
writer io.Writer // Destination for formatted output | ||
} | ||
|
||
// Parse parses golang template returning a formatter | ||
// | ||
// - OriginPodman implies text is a template from podman code. Output will | ||
// be filtered through a tabwriter. | ||
// | ||
// - OriginUser implies text is a template from a user. If template includes | ||
// keyword "table" output will be filtered through a tabwriter. | ||
func (f *Formatter) Parse(origin Origin, text string) (*Formatter, error) { | ||
f.Origin = origin | ||
|
||
switch { | ||
case strings.HasPrefix(text, "table "): | ||
f.RenderTable = true | ||
text = "{{range .}}" + NormalizeFormat(text) + "{{end -}}" | ||
case OriginUser == origin: | ||
text = EnforceRange(NormalizeFormat(text)) | ||
default: | ||
text = NormalizeFormat(text) | ||
} | ||
f.text = text | ||
|
||
if f.RenderTable || origin == OriginPodman { | ||
tw := tabwriter.NewWriter(f.writer, 12, 2, 2, ' ', tabwriter.StripEscape) | ||
f.writer = tw | ||
f.flusher = tw | ||
f.RenderHeaders = true | ||
} | ||
|
||
tmpl, err := f.template.Funcs(template.FuncMap(DefaultFuncs)).Parse(text) | ||
if err != nil { | ||
return f, err | ||
} | ||
f.template = tmpl | ||
return f, nil | ||
} | ||
|
||
// Funcs adds the elements of the argument map to the template's function map. | ||
// A default template function will be replaced if there is a key collision. | ||
func (f *Formatter) Funcs(funcMap template.FuncMap) *Formatter { | ||
m := make(template.FuncMap, len(DefaultFuncs)+len(funcMap)) | ||
for k, v := range DefaultFuncs { | ||
m[k] = v | ||
} | ||
for k, v := range funcMap { | ||
m[k] = v | ||
} | ||
f.template = f.template.Funcs(funcMap) | ||
return f | ||
} | ||
|
||
// Init either resets the given tabwriter with new values or wraps w in tabwriter with given values | ||
func (f *Formatter) Init(w io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Formatter { | ||
flags |= tabwriter.StripEscape | ||
|
||
if tw, ok := f.writer.(*tabwriter.Writer); ok { | ||
tw = tw.Init(w, minwidth, tabwidth, padding, padchar, flags) | ||
f.writer = tw | ||
f.flusher = tw | ||
} else { | ||
tw = tabwriter.NewWriter(w, minwidth, tabwidth, padding, padchar, flags) | ||
f.writer = tw | ||
f.flusher = tw | ||
} | ||
return f | ||
} | ||
|
||
// Execute applies a parsed template to the specified data object, | ||
// and writes the output to Formatter.Writer. | ||
func (f *Formatter) Execute(data interface{}) error { | ||
return f.template.Execute(f.writer, data) | ||
} | ||
|
||
// Flush should be called after the last call to Write to ensure | ||
// that any data buffered in the Formatter is written to output. Any | ||
// incomplete escape sequence at the end is considered | ||
// complete for formatting purposes. | ||
func (f *Formatter) Flush() error { | ||
// Indirection is required here to prevent caller from having to know when | ||
// value of Flusher may be changed. | ||
return f.flusher.Flush() | ||
} | ||
|
||
// Writer returns the embedded io.Writer from Formatter | ||
func (f *Formatter) Writer() io.Writer { | ||
return f.writer | ||
} | ||
|
||
// New allocates a new, undefined Formatter with the given name and Writer | ||
func New(output io.Writer, name string) *Formatter { | ||
f := new(Formatter) | ||
|
||
f.flusher = new(NopFlusher) | ||
if flusher, ok := output.(Flusher); ok { | ||
f.flusher = flusher | ||
} | ||
|
||
f.template = template.New(name) | ||
f.writer = output | ||
return f | ||
} |
Oops, something went wrong.