pkg/formatter/text.go (124 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package formatter import ( "bytes" "fmt" "io" "path/filepath" "strings" "text/tabwriter" "text/template" "github.com/elastic/ecctl/pkg/formatter/templates" ) var defaultTemplateFuncs = template.FuncMap{ "trim": strings.TrimSpace, "trimToLen": trimToLen, "toS3TypeConfig": toS3TypeConfig, "rpad": rpad, "rpadTrim": rpadTrim, "formatBytes": formatBytes, "tab": tab, "formatClusterBytes": formatClusterBytes, "substr": substr, "computeClusterCapacity": computeClusterCapacity, "equal": equal, "derefInt": derefInt, "derefBool": derefBool, "displayAllocator": displayAllocator, "getFailedPlanStepName": getFailedPlanStepName, "getClusterName": getClusterName, "formatTopologyInfo": formatTopologyInfo, "computePlanDuration": computePlanDuration, "getESCurrentOrPendingPlan": getESCurrentOrPendingPlan, "centiCentsToCents": centiCentsToCents, "computeApmPlanDuration": computeApmPlanDuration, "getApmFailedPlanStepName": getApmFailedPlanStepName, } const ( defaultPadding = 3 ) var ( // defaultOverrideFormat is used when no fallback has been specified in // TextConfig defaultOverrideFormat = ` {{- define "override" }}{{executeTemplate .}} {{ end }}`[1:] ) // AssetLoaderFunc loads and returns the asset for the given name. If an error // is returned, the asset could not be found or could not be loaded. type AssetLoaderFunc func(name string) ([]byte, error) // TextConfig is the configuration to use for a text formatter type TextConfig struct { // Output device where the template execution will be written. Output io.Writer // Override is the format to be used for the commands that are sent Override string // Fallback base template to use when a file asset is not found in // "text/parent/child.gotmpl" unless specified `defaultOverrideFormat` is // used. Fallback string // Padding represents the number of spaces to use in between the text // columns. Padding int // AssetLoaderFunction is the asset loader function to be used. It must // return a byte encoded Go template parseable by template.Template.Parse. AssetLoaderFunction AssetLoaderFunc } func (c *TextConfig) fillValues() { if c.Fallback == "" { c.Fallback = defaultOverrideFormat } if c.AssetLoaderFunction == nil { c.AssetLoaderFunction = templates.Asset } if c.Padding <= 0 { c.Padding = defaultPadding } } // NewText acts as the factory for formatter.Text func NewText(c *TextConfig) *Text { var templateFuncs = defaultTemplateFuncs templateFuncs["executeTemplate"] = executeTemplateFunc(c.Output, c.Override) c.fillValues() return &Text{ output: c.Output, padding: c.Padding, templater: template.New("text"), funcs: templateFuncs, override: c.Override != "", fallback: c.Fallback, assetLoaderFunc: c.AssetLoaderFunction, } } // Text formats into text type Text struct { // Output device where the template execution will be written. output io.Writer // Templater to use templater *template.Template // funcs are the funtions that are passed to the template on format funcs template.FuncMap // overrides the default template. Still relies on the template asset // to loop over the properties override bool // fallback base template to use when a file asset is not found in // "text/parent/child.gotmpl" unless specified `defaultOverrideFormat` is // used. fallback string // padding represents the number of spaces to use in between the text // columns. padding int assetLoaderFunc AssetLoaderFunc } // format formats the data according to the template // used during struct initialization func (f *Text) format(text string, data interface{}) error { t, err := f.templater.Funcs(f.funcs).Parse(text) if err != nil { return err } var name = "default" if f.override { name = "override" } w := tabwriter.NewWriter(f.output, 2, 4, 3, ' ', 0) defer w.Flush() return t.ExecuteTemplate(w, name, data) } // Name obtains the name of the formatter func (f *Text) Name() string { return f.templater.Name() } // Format is used from the cmd for conveniency, it receives a path and the data // which needs to be used in the template. Uses the name of the formatter as // the first argument of the template name. // If the asset is not found, it will default to defaultOverrideFormat func (f *Text) Format(path string, data interface{}) error { if filepath.Ext(path) == "" { path += ".gotmpl" } tformat, err := f.assetLoaderFunc(filepath.Join(f.Name(), path)) if err != nil { tformat = []byte(f.fallback) } return f.format(string(tformat), data) } func executeTemplateFunc(output io.Writer, templateFormat string) func(data interface{}) string { return func(data interface{}) string { t, err := template.New("template").Funcs(defaultTemplateFuncs).Parse(templateFormat) if err != nil { fmt.Fprintln(output, err) return "" } var b = new(bytes.Buffer) if err := t.Execute(b, data); err != nil { fmt.Fprintln(output, err) return "" } return b.String() } }