Skip to content

Instantly share code, notes, and snippets.

@anonymouse64
Created August 6, 2019 23:32
Show Gist options
  • Save anonymouse64/6b6a6280a8aca94767f32b000a63397d to your computer and use it in GitHub Desktop.
Save anonymouse64/6b6a6280a8aca94767f32b000a63397d to your computer and use it in GitHub Desktop.
// the user level header printing utility that initializes the recursion vars
// for wrapHeaderImpl
func wrapHeader(w io.Writer, name string, header interface{}, termWidth int) error {
return wrapHeaderImpl(w, name+":\t", false, header, 0, termWidth)
}
// the actual recursion implementation for a single header name + value pair
// intro in the first iteration is "name:", and becomes some combination of map
// keys for maps, whitespace, or "-" for lists in later iterations
// printIntro1 is whether or not the intro should be printed by wrapGeneric when
// we recurse to the base case with a single string - it should initially be false
// but will get set to true for 2nd and later iterations of map/list values
// numIdents is the number of indents we have performed for this single
// top-level header
// termWidth is the maximum terminal width currently configured
func wrapHeaderImpl(w io.Writer, intro string, printIntro1 bool, header interface{}, numIndents, termWidth int) error {
switch x := header.(type) {
case string:
if strings.Contains(x, "\n") {
// this case is for assertions that are long and were already
// wrapped by the assertion encoding
x = strings.Join(strings.Split(x, "\n"), "")
}
// if the intro + header value is greater than the terminal width,
// quote the string so that when it is split up into multiple lines it
// remains the same string when wrapGeneric wraps it with newlines
// in the middle
if len(x)+len(intro) > termWidth {
x = strconv.Quote(x)
}
// if we are printing the intro, then use the same intro for both
// arguments because the intro is used for indent calculation
if printIntro1 {
return wrapGeneric(w, quotedIfNeeded(x), intro, intro, printIntro1, true, termWidth)
}
// if we are not printing the first intro, then the first intro is just
// being used as indentation calculation, and the second indent, which
// is what will be output on additional lines added, should just be all
// whitespace in the additional lines of the same length as intro
return wrapGeneric(w, quotedIfNeeded(x), intro, strings.Repeat(" ", len(intro)), printIntro1, true, termWidth)
case []interface{}:
// slice of things, recursively handle each element to support lists of
// lists or lists of maps
if len(x) == 0 {
// trivial case, output the empty list
return wrapGeneric(w, []rune("[]"), intro, intro, printIntro1, true, termWidth)
}
// first print the intro
fmt.Fprintln(w, intro)
// calculate the prefix as the summation of the indentations, plus the
// list identifier on the end
prefix := strings.Repeat(" ", numIndents+1) + "- "
// handle the first element separately from the rest because the first
// element should not have the prefix printed off, because it will end
// up duplicating with the intro we already printed off
err := wrapHeaderImpl(w, prefix, false, x[0], numIndents+1, termWidth)
if err != nil {
return err
}
// for all the rest of the elements, do print off the prefix
for _, elem := range x[1:] {
err := wrapHeaderImpl(w, prefix, true, elem, numIndents+1, termWidth)
if err != nil {
return err
}
}
case map[string]interface{}:
// map of things, recursively handle key's value to support maps of
// lists or maps or maps
if len(x) == 0 {
// trivial case, output the empty map
return wrapGeneric(w, []rune("{}"), intro, intro, printIntro1, true, termWidth)
}
// first print the intro
fmt.Fprintln(w, intro)
// make a list of the keys so we always traverse in lexographic order
xElemNames := make([]string, len(x))
i := 0
for k := range x {
xElemNames[i] = k
i++
}
sort.Strings(xElemNames)
// special case the first element - it needs to have the prefix output
// and also needs to use the same prefix for indent calculation
prefix := strings.Repeat(" ", numIndents+1) + xElemNames[0] + ": "
wrapHeaderImpl(w, prefix, false, x[xElemNames[0]], numIndents+1, termWidth)
// for all the rest of the elements, don't output the prefix in
// following lines
for _, k := range xElemNames[1:] {
v := x[k]
prefix := strings.Repeat(" ", numIndents+1) + k + ": "
wrapHeaderImpl(w, prefix, true, v, numIndents+1, termWidth)
}
default:
return fmt.Errorf("invalid type %T for assertion header", header)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment