Created
August 6, 2019 23:32
-
-
Save anonymouse64/6b6a6280a8aca94767f32b000a63397d to your computer and use it in GitHub Desktop.
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
// 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