func newPrinter(format, order, tpl string, noHeaders bool) (Printer, error) {
	var printer Printer
	switch format {
	case "yaml":
		printer = &YAMLPrinter{}
	case "json":
		printer = &JSONPrinter{}
	case "table", "wide", "markdown":
		printer = newTablePrinter(format, order, noHeaders, nil)
	case "template":
		tmpl, err := template.New("").Parse(tpl)
		if err != nil {
			return nil, fmt.Errorf("template invalid:%w", err)
		}
		printer = newTablePrinter(format, order, true, tmpl)
	default:
		return nil, fmt.Errorf("unknown format:%s", format)
	}
	return printer, nil
}
NewPrinter returns a suitable stdout printer for the given format
package output
import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"text/template"
	"time"
	"bytes"
	"github.com/dustin/go-humanize"
	"github.com/fatih/color"
	"github.com/metal-stack/metal-go/api/models"
	"github.com/metal-stack/metal-lib/pkg/tag"
	"github.com/metal-stack/metalctl/pkg/api"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/viper"
	"gopkg.in/yaml.v3"
)
const (
	bark            = "🚧"
	circle          = "↻"
	dot             = "●"
	exclamationMark = "❗"
	lock            = "🔒"
	nbr             = " "
	question        = "❓"
	skull           = "💀"
)
type (
	Printer interface {
		Print(data interface{}) error
	}
	JSONPrinter struct{}
	YAMLPrinter struct{}
	TablePrinter struct {
		table       *tablewriter.Table
		wide        bool
		order       string
		noHeaders   bool
		template    *template.Template
		shortHeader []string
		wideHeader  []string
		shortData   [][]string
		wideData    [][]string
	}
	MetalMachineTablePrinter struct {
		TablePrinter
	}
	MetalMachineLogsPrinter struct {
		TablePrinter
	}
	MetalMachineIssuesTablePrinter struct {
		TablePrinter
	}
	MetalFirewallTablePrinter struct {
		TablePrinter
	}
	MetalMachineAllocationTablePrinter struct {
		TablePrinter
	}
	MetalSizeTablePrinter struct {
		TablePrinter
	}
	MetalSizeMatchingLogTablePrinter struct {
		TablePrinter
	}
	MetalImageTablePrinter struct {
		TablePrinter
	}
	MetalPartitionTablePrinter struct {
		TablePrinter
	}
	MetalPartitionCapacityTablePrinter struct {
		TablePrinter
	}
	MetalSwitchTablePrinter struct {
		TablePrinter
	}
	MetalNetworkTablePrinter struct {
		TablePrinter
	}
	MetalIPTablePrinter struct {
		TablePrinter
	}
	MetalProjectTablePrinter struct {
		TablePrinter
	}
	MachineWithIPMIPrinter struct {
		TablePrinter
	}
	MetalFirmwaresPrinter struct {
		TablePrinter
	}
	FilesystemLayoutPrinter struct {
		TablePrinter
	}
	ContextPrinter struct {
		TablePrinter
	}
)
func New() Printer {
	printer, err := newPrinter(
		viper.GetString("output-format"),
		viper.GetString("order"),
		viper.GetString("template"),
		viper.GetBool("no-headers"),
	)
	if err != nil {
		log.Fatalf("unable to initialize printer:%v", err)
	}
	if viper.IsSet("force-color") {
		enabled := viper.GetBool("force-color")
		if enabled {
			color.NoColor = false
		} else {
			color.NoColor = true
		}
	}
	return printer
}
func (t *TablePrinter) render() {
	if t.template != nil {
		rows := t.shortData
		if t.wide {
			rows = t.wideData
		}
		for _, row := range rows {
			if len(row) < 1 {
				continue
			}
			fmt.Println(row[0])
		}
		return
	}
	if !t.noHeaders {
		if t.wide {
			t.table.SetHeader(t.wideHeader)
		} else {
			t.table.SetHeader(t.shortHeader)
		}
	}
	if t.wide {
		t.table.AppendBulk(t.wideData)
	} else {
		t.table.AppendBulk(t.shortData)
	}
	t.table.Render()
}
func (t *TablePrinter) addShortData(row []string, data interface{}) {
	if t.wide {
		return
	}
	t.shortData = append(t.shortData, t.rowOrTemplate(row, data))
}
func (t *TablePrinter) addWideData(row []string, data interface{}) {
	if !t.wide {
		return
	}
	t.wideData = append(t.wideData, t.rowOrTemplate(row, data))
}
func (t *TablePrinter) rowOrTemplate(row []string, data interface{}) []string {
	tpl := t.template
	if tpl != nil {
		var buf bytes.Buffer
		err := tpl.Execute(&buf, genericObject(data))
		if err != nil {
			fmt.Printf("unable to parse template:%v", err)
			os.Exit(1)
		}
		return []string{buf.String()}
	}
	return row
}