40 Star 146 Fork 3

Gitee 极速下载/grafana

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/grafana/grafana
克隆/下载
formatter_json.go 10.56 KB
一键复制 编辑 原始数据 按行查看 历史
package dashdiffs
import (
"bytes"
"errors"
"fmt"
"html/template"
"sort"
diff "github.com/yudai/gojsondiff"
)
type ChangeType int
const (
ChangeNil ChangeType = iota
ChangeAdded
ChangeDeleted
ChangeOld
ChangeNew
ChangeUnchanged
)
var (
// changeTypeToSymbol is used for populating the terminating characer in
// the diff
changeTypeToSymbol = map[ChangeType]string{
ChangeNil: "",
ChangeAdded: "+",
ChangeDeleted: "-",
ChangeOld: "-",
ChangeNew: "+",
}
// changeTypeToName is used for populating class names in the diff
changeTypeToName = map[ChangeType]string{
ChangeNil: "same",
ChangeAdded: "added",
ChangeDeleted: "deleted",
ChangeOld: "old",
ChangeNew: "new",
}
)
var (
// tplJSONDiffWrapper is the template that wraps a diff
tplJSONDiffWrapper = `{{ define "JSONDiffWrapper" -}}
{{ range $index, $element := . }}
{{ template "JSONDiffLine" $element }}
{{ end }}
{{ end }}`
// tplJSONDiffLine is the template that prints each line in a diff
tplJSONDiffLine = `{{ define "JSONDiffLine" -}}
<p id="l{{ .LineNum }}" class="diff-line diff-json-{{ cton .Change }}">
<span class="diff-line-number">
{{if .LeftLine }}{{ .LeftLine }}{{ end }}
</span>
<span class="diff-line-number">
{{if .RightLine }}{{ .RightLine }}{{ end }}
</span>
<span class="diff-value diff-indent-{{ .Indent }}" title="{{ .Text }}">
{{ .Text }}
</span>
<span class="diff-line-icon">{{ ctos .Change }}</span>
</p>
{{ end }}`
)
var diffTplFuncs = template.FuncMap{
"ctos": func(c ChangeType) string {
if symbol, ok := changeTypeToSymbol[c]; ok {
return symbol
}
return ""
},
"cton": func(c ChangeType) string {
if name, ok := changeTypeToName[c]; ok {
return name
}
return ""
},
}
// JSONLine contains the data required to render each line of the JSON diff
// and contains the data required to produce the tokens output in the basic
// diff.
type JSONLine struct {
LineNum int `json:"line"`
LeftLine int `json:"leftLine"`
RightLine int `json:"rightLine"`
Indent int `json:"indent"`
Text string `json:"text"`
Change ChangeType `json:"changeType"`
Key string `json:"key"`
Val interface{} `json:"value"`
}
func NewJSONFormatter(left interface{}) *JSONFormatter {
tpl := template.Must(template.New("JSONDiffWrapper").Funcs(diffTplFuncs).Parse(tplJSONDiffWrapper))
tpl = template.Must(tpl.New("JSONDiffLine").Funcs(diffTplFuncs).Parse(tplJSONDiffLine))
return &JSONFormatter{
left: left,
Lines: []*JSONLine{},
tpl: tpl,
path: []string{},
size: []int{},
lineCount: 0,
inArray: []bool{},
}
}
type JSONFormatter struct {
left interface{}
path []string
size []int
inArray []bool
lineCount int
leftLine int
rightLine int
line *AsciiLine
Lines []*JSONLine
tpl *template.Template
}
type AsciiLine struct {
// the type of change
change ChangeType
// the actual changes - no formatting
key string
val interface{}
// level of indentation for the current line
indent int
// buffer containing the fully formatted line
buffer *bytes.Buffer
}
func (f *JSONFormatter) Format(diff diff.Diff) (result string, err error) {
if v, ok := f.left.(map[string]interface{}); ok {
f.formatObject(v, diff)
} else if v, ok := f.left.([]interface{}); ok {
f.formatArray(v, diff)
} else {
return "", fmt.Errorf("expected map[string]interface{} or []interface{}, got %T",
f.left)
}
b := &bytes.Buffer{}
err = f.tpl.ExecuteTemplate(b, "JSONDiffWrapper", f.Lines)
if err != nil {
fmt.Printf("%v\n", err)
return "", err
}
return b.String(), nil
}
func (f *JSONFormatter) formatObject(left map[string]interface{}, df diff.Diff) {
f.addLineWith(ChangeNil, "{")
f.push("ROOT", len(left), false)
f.processObject(left, df.Deltas())
f.pop()
f.addLineWith(ChangeNil, "}")
}
func (f *JSONFormatter) formatArray(left []interface{}, df diff.Diff) {
f.addLineWith(ChangeNil, "[")
f.push("ROOT", len(left), true)
f.processArray(left, df.Deltas())
f.pop()
f.addLineWith(ChangeNil, "]")
}
func (f *JSONFormatter) processArray(array []interface{}, deltas []diff.Delta) error {
patchedIndex := 0
for index, value := range array {
f.processItem(value, deltas, diff.Index(index))
patchedIndex++
}
// additional Added
for _, delta := range deltas {
switch delta.(type) {
case *diff.Added:
d := delta.(*diff.Added)
// skip items already processed
if int(d.Position.(diff.Index)) < len(array) {
continue
}
f.printRecursive(d.Position.String(), d.Value, ChangeAdded)
}
}
return nil
}
func (f *JSONFormatter) processObject(object map[string]interface{}, deltas []diff.Delta) error {
names := sortKeys(object)
for _, name := range names {
value := object[name]
f.processItem(value, deltas, diff.Name(name))
}
// Added
for _, delta := range deltas {
switch delta.(type) {
case *diff.Added:
d := delta.(*diff.Added)
f.printRecursive(d.Position.String(), d.Value, ChangeAdded)
}
}
return nil
}
func (f *JSONFormatter) processItem(value interface{}, deltas []diff.Delta, position diff.Position) error {
matchedDeltas := f.searchDeltas(deltas, position)
positionStr := position.String()
if len(matchedDeltas) > 0 {
for _, matchedDelta := range matchedDeltas {
switch matchedDelta.(type) {
case *diff.Object:
d := matchedDelta.(*diff.Object)
switch value.(type) {
case map[string]interface{}:
//ok
default:
return errors.New("Type mismatch")
}
o := value.(map[string]interface{})
f.newLine(ChangeNil)
f.printKey(positionStr)
f.print("{")
f.closeLine()
f.push(positionStr, len(o), false)
f.processObject(o, d.Deltas)
f.pop()
f.newLine(ChangeNil)
f.print("}")
f.printComma()
f.closeLine()
case *diff.Array:
d := matchedDelta.(*diff.Array)
switch value.(type) {
case []interface{}:
//ok
default:
return errors.New("Type mismatch")
}
a := value.([]interface{})
f.newLine(ChangeNil)
f.printKey(positionStr)
f.print("[")
f.closeLine()
f.push(positionStr, len(a), true)
f.processArray(a, d.Deltas)
f.pop()
f.newLine(ChangeNil)
f.print("]")
f.printComma()
f.closeLine()
case *diff.Added:
d := matchedDelta.(*diff.Added)
f.printRecursive(positionStr, d.Value, ChangeAdded)
f.size[len(f.size)-1]++
case *diff.Modified:
d := matchedDelta.(*diff.Modified)
savedSize := f.size[len(f.size)-1]
f.printRecursive(positionStr, d.OldValue, ChangeOld)
f.size[len(f.size)-1] = savedSize
f.printRecursive(positionStr, d.NewValue, ChangeNew)
case *diff.TextDiff:
savedSize := f.size[len(f.size)-1]
d := matchedDelta.(*diff.TextDiff)
f.printRecursive(positionStr, d.OldValue, ChangeOld)
f.size[len(f.size)-1] = savedSize
f.printRecursive(positionStr, d.NewValue, ChangeNew)
case *diff.Deleted:
d := matchedDelta.(*diff.Deleted)
f.printRecursive(positionStr, d.Value, ChangeDeleted)
default:
return errors.New("Unknown Delta type detected")
}
}
} else {
f.printRecursive(positionStr, value, ChangeUnchanged)
}
return nil
}
func (f *JSONFormatter) searchDeltas(deltas []diff.Delta, postion diff.Position) (results []diff.Delta) {
results = make([]diff.Delta, 0)
for _, delta := range deltas {
switch delta.(type) {
case diff.PostDelta:
if delta.(diff.PostDelta).PostPosition() == postion {
results = append(results, delta)
}
case diff.PreDelta:
if delta.(diff.PreDelta).PrePosition() == postion {
results = append(results, delta)
}
default:
panic("heh")
}
}
return
}
func (f *JSONFormatter) push(name string, size int, array bool) {
f.path = append(f.path, name)
f.size = append(f.size, size)
f.inArray = append(f.inArray, array)
}
func (f *JSONFormatter) pop() {
f.path = f.path[0 : len(f.path)-1]
f.size = f.size[0 : len(f.size)-1]
f.inArray = f.inArray[0 : len(f.inArray)-1]
}
func (f *JSONFormatter) addLineWith(change ChangeType, value string) {
f.line = &AsciiLine{
change: change,
indent: len(f.path),
buffer: bytes.NewBufferString(value),
}
f.closeLine()
}
func (f *JSONFormatter) newLine(change ChangeType) {
f.line = &AsciiLine{
change: change,
indent: len(f.path),
buffer: bytes.NewBuffer([]byte{}),
}
}
func (f *JSONFormatter) closeLine() {
leftLine := 0
rightLine := 0
f.lineCount++
switch f.line.change {
case ChangeAdded, ChangeNew:
f.rightLine++
rightLine = f.rightLine
case ChangeDeleted, ChangeOld:
f.leftLine++
leftLine = f.leftLine
case ChangeNil, ChangeUnchanged:
f.rightLine++
f.leftLine++
rightLine = f.rightLine
leftLine = f.leftLine
}
s := f.line.buffer.String()
f.Lines = append(f.Lines, &JSONLine{
LineNum: f.lineCount,
RightLine: rightLine,
LeftLine: leftLine,
Indent: f.line.indent,
Text: s,
Change: f.line.change,
Key: f.line.key,
Val: f.line.val,
})
}
func (f *JSONFormatter) printKey(name string) {
if !f.inArray[len(f.inArray)-1] {
f.line.key = name
fmt.Fprintf(f.line.buffer, `"%s": `, name)
}
}
func (f *JSONFormatter) printComma() {
f.size[len(f.size)-1]--
if f.size[len(f.size)-1] > 0 {
f.line.buffer.WriteRune(',')
}
}
func (f *JSONFormatter) printValue(value interface{}) {
switch value.(type) {
case string:
f.line.val = value
fmt.Fprintf(f.line.buffer, `"%s"`, value)
case nil:
f.line.val = "null"
f.line.buffer.WriteString("null")
default:
f.line.val = value
fmt.Fprintf(f.line.buffer, `%#v`, value)
}
}
func (f *JSONFormatter) print(a string) {
f.line.buffer.WriteString(a)
}
func (f *JSONFormatter) printRecursive(name string, value interface{}, change ChangeType) {
switch value.(type) {
case map[string]interface{}:
f.newLine(change)
f.printKey(name)
f.print("{")
f.closeLine()
m := value.(map[string]interface{})
size := len(m)
f.push(name, size, false)
keys := sortKeys(m)
for _, key := range keys {
f.printRecursive(key, m[key], change)
}
f.pop()
f.newLine(change)
f.print("}")
f.printComma()
f.closeLine()
case []interface{}:
f.newLine(change)
f.printKey(name)
f.print("[")
f.closeLine()
s := value.([]interface{})
size := len(s)
f.push("", size, true)
for _, item := range s {
f.printRecursive("", item, change)
}
f.pop()
f.newLine(change)
f.print("]")
f.printComma()
f.closeLine()
default:
f.newLine(change)
f.printKey(name)
f.printValue(value)
f.printComma()
f.closeLine()
}
}
func sortKeys(m map[string]interface{}) (keys []string) {
keys = make([]string, 0, len(m))
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
return
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
JavaScript
1
https://gitee.com/mirrors/grafana.git
git@gitee.com:mirrors/grafana.git
mirrors
grafana
grafana
v4.5.0-beta1

搜索帮助