func parseFieldNameFormat(fFmt string) (nameFrom, formatAs string) {
	vals := strings.Split(fFmt, "as")
	if len(vals) == 1 {
		nameFrom = strings.TrimSpace(vals[0])
	}
	if len(vals) == 2 {
		nameFrom = strings.TrimSpace(vals[0])
		formatAs = strings.TrimSpace(vals[1])
	}
	switch nameFrom {
	case "field", "env", "flag", "toml", "yaml", "json":
		break
	default:
		nameFrom = "field"
	}
	switch formatAs {
	case "screaming-snake", "snake", "camel", "lower-camel", "screaming-kebab", "kebab", "lower", "field":
		break
	case "":
		switch nameFrom {
		case "env":
			formatAs = "screaming-snake"
		case "flag":
			formatAs = "kebab"
		case "toml":
			formatAs = "snake"
		case "yaml":
			formatAs = "lower"
		case "json":
			formatAs = "snake"  
		default:
			formatAs = "field"
		}
	default:
		formatAs = "field"
	}
	return nameFrom, formatAs
}
parseFieldNameFormat

fFmt general form:
"{nameFrom}[ as {formatAs}]"

nameFrom values:
- "field" (name is based on the struct field - default)
- "env" (name based on env input)
- "flag" (name based on flag input)
- "toml" (name based on toml input)
- "yaml" (name based on yaml input)
- "json" (name based on json input)

formatAs values:
- "screaming-snake"
- "snake"
- "camel"
- "lower-camel"
- "screaming-kebab"
- "kebab"
- "lower" (everything lowercase squished together)
- "field" (name matches the struct field name)

Examples:

"" (empty)     -> "field", "field"
"env"          -> "env", "screaming-snake"
"flag"         -> "flag", "kebab"
"toml"         -> "toml", "snake"
"yaml"         -> "yaml", "lower"
"json"         -> "json", "snake"
"field"        -> "field", "field"
"as snake"     -> "field", "snake"
"env as snake" -> "env", "snake"

Incorrect input format will result in the default values "field", "field".
package render
import (
	"bytes"
	"fmt"
	"strings"
	"github.com/pcelvng/go-config/util"
	"github.com/pcelvng/go-config/util/node"
)
var (
	configTag = "config"
	reqTag    = "req"
	showTag   = "show"
	fmtTag    = "fmt"
	ignoreTag = "ignore"
	helpTag   = "help"
	sepTag    = "sep"
	flagTag   = "flag"
	envTag    = "env"
	jsonTag   = "json"
	yamlTag   = "yaml"
	tomlTag   = "toml"
	defaultSep = ","
)
func New(o Options, nGrps []*node.Nodes) (*Renderer, error) {
	var err error
	r := &Renderer{
		preamble:   o.Preamble,
		conclusion: o.Postamble,
	}
	ng := defNameGenerator{}
	ng.nameFrom, ng.formatAs = parseFieldNameFormat(o.FieldNameFormat)
	r.nameFunc = ng.genFieldName
	r.renderFunc = defaultRenderer
	if o.RenderFunc != nil {
		r.renderFunc = o.RenderFunc
	}
	r.fGrps, err = r.fieldGroups(nGrps)
	if err != nil {
		return nil, err
	}
	r.recordVals()
	return r, nil
}
type Field struct {
	Name        string
	ValueBefore string
	ValueAfter  string
	Type        string
	Req         bool
	Show        bool
	TimeFmt     string  
	Node          *node.Node
	valueRecorded bool
}
func (f *Field) recordValue() {
	if f.valueRecorded {
		f.ValueAfter = toStr(f.Node)
		return
	}
	f.ValueBefore = toStr(f.Node)
	f.valueRecorded = true
}
func (f *Field) IsZero(val string) bool {
	switch f.Type {
	case "bool":
		return val == "false"
	case "int", "uint", "float":
		return val == "0"
	case "string", "time":
		return val == ""
	case "bools", "durations", "ints", "uints", "strings":
		return val == "[]" || val == ""
	case "duration":
		return val == "0" || val == "0s"
	default:
		return false
	}
}
type Options struct {
	Preamble string
	Postamble string
	FieldNameFormat string
	RenderFunc RenderFunc
}
type RenderFunc func(preamble, conclusion string, fieldGroups [][]*Field) []byte
type Renderer struct {
	preamble   string      
	conclusion string      
	fGrps      [][]*Field  
	renderFunc RenderFunc
	nameFunc   func(n *node.Node, heritage []*node.Node) string
}
func (r *Renderer) Render() []byte {
	r.recordVals()  
	return r.renderFunc(r.preamble, r.conclusion, r.fGrps)
}
func (r *Renderer) recordVals() {
	for _, fGrp := range r.fGrps {
		for _, f := range fGrp {
			f.recordValue()
		}
	}
}
func defaultRenderer(preamble, conclusion string, fieldGroups [][]*Field) []byte {
	cols := 175
	buf := new(bytes.Buffer)
	if preamble != "" {
		fmt.Fprintln(buf, preamble)
	}
	for _, fg := range fieldGroups {
		lines := make([]string, 0, len(fg))
		maxlen := 0
		for _, f := range fg {
			line := ""
			line += f.Name + " (" + f.Type + ")" + ":\x00"
			if len(line) > maxlen {
				maxlen = len(line)
			}
			if f.Show {
				if f.Type == "string" {
					line += fmt.Sprintf("%q", f.ValueAfter)
				} else {
					line += fmt.Sprintf("%s", f.ValueAfter)
				}
			} else {
				line += "[redacted]"
			}
			if !f.IsZero(f.ValueBefore) && f.Show {
				if f.Type == "string" {
					line += fmt.Sprintf(" (default: %q)", f.ValueBefore)
				} else {
					line += fmt.Sprintf(" (default: %s)", f.ValueBefore)
				}
			}
			if f.Req {
				line += " (required)"
			}
			lines = append(lines, line)
		}
		for _, line := range lines {
			sidx := strings.Index(line, "\x00")
			spacing := strings.Repeat(" ", maxlen-sidx)
			fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:]))
		}
		fmt.Fprintln(buf, "")  
	}
	if conclusion != "" {
		fmt.Fprintln(buf, conclusion)
	}
	body := strings.TrimSpace(buf.String())
	return []byte("\r\n" + body + "\r\n")
}
func wrap(i, w int, s string) string {
	if w == 0 {
		return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1)
	}
	wrap := w - i
	var r, l string
	if wrap < 24 {
		i = 16
		wrap = w - i
		r += "\n" + strings.Repeat(" ", i)
	}
	if wrap < 24 {
		return strings.Replace(s, "\n", r, -1)
	}
	slop := 5
	wrap = wrap - slop
	l, s = wrapN(wrap, slop, s)
	r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1)
	for s != "" {
		var t string
		t, s = wrapN(wrap, slop, s)
		r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1)
	}
	return r
}
func wrapN(i, slop int, s string) (string, string) {
	if i+slop > len(s) {
		return s, ""
	}
	w := strings.LastIndexAny(s[:i], " \t\n")
	if w <= 0 {
		return s, ""
	}
	nlPos := strings.LastIndex(s[:i], "\n")
	if nlPos > 0 && nlPos < w {
		return s[:nlPos], s[nlPos+1:]
	}
	return s[:w], s[w+1:]
}
func (r *Renderer) fieldGroups(ngrps []*node.Nodes) ([][]*Field, error) {
	fgs := make([][]*Field, 0)
	for _, ngrp := range ngrps {
		fg, err := r.fieldGroup(ngrp)
		if err != nil {
			return nil, err
		}
		fgs = append(fgs, fg)
	}
	return fgs, nil
}
func (r *Renderer) fieldGroup(ngrp *node.Nodes) ([]*Field, error) {
	fg := make([]*Field, 0)
	for _, n := range ngrp.List() {
		heritage := node.Parents(n, ngrp.Map())
		if isAnyIgnored(append(heritage, n)) {
			continue
		}
		if n.IsStruct() && !n.IsTime() {
			continue
		}
		switch "omitprefix" {
		case n.GetTag(envTag), n.GetTag(flagTag), n.GetTag(tomlTag), n.GetTag(yamlTag), n.GetTag(jsonTag):
			return nil, fmt.Errorf("'omitprefix' cannot be used on non-struct field types")
		}
		name := r.nameFunc(n, heritage)
		if name == "" {
			continue
		}
		fg = append(fg, &Field{
			Name:    name,
			Type:    node.ValueType(n),
			Req:     n.GetBoolTag(reqTag),
			Show:    isShown(n),
			TimeFmt: timeFmt(n),
			Node:    n,
		})
	}
	return fg, nil
}
func toStr(n *node.Node) string {
	if n.IsTime() {
		return n.TimeString(n.GetTag(fmtTag))
	} else if n.IsSlice() {
		vals := n.SliceString()
		return `[` + strings.Join(vals, getSep(n)) + `]`
	}
	val := n.String()
	return val
}
func getSep(n *node.Node) string {
	sep := n.GetTag(sepTag)
	if sep == "" {
		sep = defaultSep
	}
	return sep
}
func isShown(n *node.Node) bool {
	show := n.GetTag(showTag)
	if show == "" {
		return true
	}
	return n.GetBoolTag(showTag)
}
func timeFmt(n *node.Node) string {
	fmtV := n.GetTag(fmtTag)
	if fmtV == "" {
		fmtV = node.NormTimeFormat("")
	}
	return fmtV
}