synapsecns/sanguine

View on GitHub
core/ginhelper/parse.go

Summary

Maintainability
A
0 mins
Test Coverage
package ginhelper

import (
    "bytes"
    "encoding/json"
    "fmt"
    "sort"
    "strconv"
    "strings"
)

// StringMaxLength is the maximum length of a string to display.
var StringMaxLength = 0

// Newline is the string to use for newlines.
var Newline = "\n"

// Indent is the number of spaces to indent.
var Indent = 4

// BeautifyJSONBytes beautifies json bytes.
func BeautifyJSONBytes(data []byte, hiddenFields []string) ([]byte, error) {
    var v interface{}
    if err := json.Unmarshal(data, &v); err != nil {
        return nil, fmt.Errorf("failed to unmarshal json: %w", err)
    }

    v = removeHiddenFields(v, hiddenFields)

    return []byte(format(v, 1)), nil
}

// FormatToBeautifulJSON dumps v to beautified json bytes.
func FormatToBeautifulJSON(v interface{}, hiddenFields []string) ([]byte, error) {
    data, err := json.Marshal(v)
    if err != nil {
        return nil, fmt.Errorf("failed to marshal json: %w", err)
    }
    return BeautifyJSONBytes(data, hiddenFields)
}

func format(v interface{}, depth int) string {
    switch val := v.(type) {
    case string:
        return formatString(val)
    case float64:
        return fmt.Sprint(strconv.FormatFloat(val, 'f', -1, 64))
    case bool:
        return fmt.Sprint(strconv.FormatBool(val))
    case nil:
        return "null"
    case map[string]interface{}:
        return formatMap(val, depth)
    case []interface{}:
        return formatArray(val, depth)
    }

    return ""
}

func formatString(s string) string {
    r := []rune(s)
    if StringMaxLength != 0 && len(r) >= StringMaxLength {
        s = string(r[0:StringMaxLength]) + "..."
    }

    buf := &bytes.Buffer{}
    encoder := json.NewEncoder(buf)
    encoder.SetEscapeHTML(false)
    // TODO: check error
    //nolint:errchkjson
    _ = encoder.Encode(s)
    s = buf.String()
    s = strings.TrimSuffix(s, "\n")

    return fmt.Sprint(s)
}

func formatMap(m map[string]interface{}, depth int) string {
    if len(m) == 0 {
        return "{}"
    }

    currentIndent := generateIndent(depth - 1)
    nextIndent := generateIndent(depth)
    rows := []string{}
    keys := []string{}

    for key := range m {
        keys = append(keys, key)
    }

    sort.Strings(keys)

    for _, key := range keys {
        val := m[key]
        k := fmt.Sprintf(`"%s"`, key)
        v := format(val, depth+1)

        valueIndent := " "
        if Newline == "" {
            valueIndent = ""
        }
        row := fmt.Sprintf("%s%s:%s%s", nextIndent, k, valueIndent, v)
        rows = append(rows, row)
    }

    return fmt.Sprintf("{%s%s%s%s}", Newline, strings.Join(rows, ","+Newline), Newline, currentIndent)
}

func formatArray(a []interface{}, depth int) string {
    if len(a) == 0 {
        return "[]"
    }

    currentIndent := generateIndent(depth - 1)
    nextIndent := generateIndent(depth)
    rows := []string{}

    for _, val := range a {
        c := format(val, depth+1)
        row := nextIndent + c
        rows = append(rows, row)
    }
    return fmt.Sprintf("[%s%s%s%s]", Newline, strings.Join(rows, ","+Newline), Newline, currentIndent)
}

func generateIndent(depth int) string {
    return strings.Repeat(" ", Indent*depth)
}

func removeHiddenFields(v interface{}, hiddenFields []string) interface{} {
    if _, ok := v.(map[string]interface{}); !ok {
        return v
    }

    // nolint: forcetypeassert
    m := v.(map[string]interface{})

    // case insensitive key deletion
    for _, hiddenField := range hiddenFields {
        for k := range m {
            if strings.EqualFold(k, hiddenField) {
                delete(m, k)
            }
        }
    }

    return m
}