mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
	
	
		
			335 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			335 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | package dashdiffs | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"html/template" | ||
|  | 
 | ||
|  | 	diff "github.com/yudai/gojsondiff" | ||
|  | ) | ||
|  | 
 | ||
|  | // A BasicDiff holds the stateful values that are used when generating a basic
 | ||
|  | // diff from JSON tokens.
 | ||
|  | type BasicDiff struct { | ||
|  | 	narrow     string | ||
|  | 	keysIdent  int | ||
|  | 	writing    bool | ||
|  | 	LastIndent int | ||
|  | 	Block      *BasicBlock | ||
|  | 	Change     *BasicChange | ||
|  | 	Summary    *BasicSummary | ||
|  | } | ||
|  | 
 | ||
|  | // A BasicBlock represents a top-level element in a basic diff.
 | ||
|  | type BasicBlock struct { | ||
|  | 	Title     string | ||
|  | 	Old       interface{} | ||
|  | 	New       interface{} | ||
|  | 	Change    ChangeType | ||
|  | 	Changes   []*BasicChange | ||
|  | 	Summaries []*BasicSummary | ||
|  | 	LineStart int | ||
|  | 	LineEnd   int | ||
|  | } | ||
|  | 
 | ||
|  | // A BasicChange represents the change from an old to new value. There are many
 | ||
|  | // BasicChanges in a BasicBlock.
 | ||
|  | type BasicChange struct { | ||
|  | 	Key       string | ||
|  | 	Old       interface{} | ||
|  | 	New       interface{} | ||
|  | 	Change    ChangeType | ||
|  | 	LineStart int | ||
|  | 	LineEnd   int | ||
|  | } | ||
|  | 
 | ||
|  | // A BasicSummary represents the changes within a basic block that're too deep
 | ||
|  | // or verbose to be represented in the top-level BasicBlock element, or in the
 | ||
|  | // BasicChange. Instead of showing the values in this case, we simply print
 | ||
|  | // the key and count how many times the given change was applied to that
 | ||
|  | // element.
 | ||
|  | type BasicSummary struct { | ||
|  | 	Key       string | ||
|  | 	Change    ChangeType | ||
|  | 	Count     int | ||
|  | 	LineStart int | ||
|  | 	LineEnd   int | ||
|  | } | ||
|  | 
 | ||
|  | type BasicFormatter struct { | ||
|  | 	jsonDiff *JSONFormatter | ||
|  | 	tpl      *template.Template | ||
|  | } | ||
|  | 
 | ||
|  | func NewBasicFormatter(left interface{}) *BasicFormatter { | ||
|  | 	tpl := template.Must(template.New("block").Funcs(tplFuncMap).Parse(tplBlock)) | ||
|  | 	tpl = template.Must(tpl.New("change").Funcs(tplFuncMap).Parse(tplChange)) | ||
|  | 	tpl = template.Must(tpl.New("summary").Funcs(tplFuncMap).Parse(tplSummary)) | ||
|  | 
 | ||
|  | 	return &BasicFormatter{ | ||
|  | 		jsonDiff: NewJSONFormatter(left), | ||
|  | 		tpl:      tpl, | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (b *BasicFormatter) Format(d diff.Diff) ([]byte, error) { | ||
|  | 	// calling jsonDiff.Format(d) populates the JSON diff's "Lines" value,
 | ||
|  | 	// which we use to compute the basic dif
 | ||
|  | 	_, err := b.jsonDiff.Format(d) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	bd := &BasicDiff{} | ||
|  | 	blocks := bd.Basic(b.jsonDiff.Lines) | ||
|  | 	buf := &bytes.Buffer{} | ||
|  | 
 | ||
|  | 	err = b.tpl.ExecuteTemplate(buf, "block", blocks) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 	return buf.Bytes(), nil | ||
|  | } | ||
|  | 
 | ||
|  | // Basic is V2 of the basic diff
 | ||
|  | func (b *BasicDiff) Basic(lines []*JSONLine) []*BasicBlock { | ||
|  | 	// init an array you can append to for the basic "blocks"
 | ||
|  | 	blocks := make([]*BasicBlock, 0) | ||
|  | 
 | ||
|  | 	// iterate through each line
 | ||
|  | 	for _, line := range lines { | ||
|  | 		if b.LastIndent == 3 && line.Indent == 2 && line.Change == ChangeNil { | ||
|  | 			if b.Block != nil { | ||
|  | 				blocks = append(blocks, b.Block) | ||
|  | 			} | ||
|  | 		} | ||
|  | 		b.LastIndent = line.Indent | ||
|  | 
 | ||
|  | 		if line.Indent == 2 { | ||
|  | 			switch line.Change { | ||
|  | 			case ChangeNil: | ||
|  | 				if line.Change == ChangeNil { | ||
|  | 					if line.Key != "" { | ||
|  | 						b.Block = &BasicBlock{ | ||
|  | 							Title:  line.Key, | ||
|  | 							Change: line.Change, | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 			case ChangeAdded, ChangeDeleted: | ||
|  | 				blocks = append(blocks, &BasicBlock{ | ||
|  | 					Title:     line.Key, | ||
|  | 					Change:    line.Change, | ||
|  | 					New:       line.Val, | ||
|  | 					LineStart: line.LineNum, | ||
|  | 				}) | ||
|  | 
 | ||
|  | 			case ChangeOld: | ||
|  | 				b.Block = &BasicBlock{ | ||
|  | 					Title:     line.Key, | ||
|  | 					Old:       line.Val, | ||
|  | 					Change:    line.Change, | ||
|  | 					LineStart: line.LineNum, | ||
|  | 				} | ||
|  | 
 | ||
|  | 			case ChangeNew: | ||
|  | 				b.Block.New = line.Val | ||
|  | 				b.Block.LineEnd = line.LineNum | ||
|  | 
 | ||
|  | 				// then write out the change
 | ||
|  | 				blocks = append(blocks, b.Block) | ||
|  | 			default: | ||
|  | 				// ok
 | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Other Lines
 | ||
|  | 		if line.Indent > 2 { | ||
|  | 			// Ensure single line change
 | ||
|  | 			if line.Key != "" && line.Val != nil && !b.writing { | ||
|  | 				switch line.Change { | ||
|  | 				case ChangeAdded, ChangeDeleted: | ||
|  | 					b.Block.Changes = append(b.Block.Changes, &BasicChange{ | ||
|  | 						Key:       line.Key, | ||
|  | 						Change:    line.Change, | ||
|  | 						New:       line.Val, | ||
|  | 						LineStart: line.LineNum, | ||
|  | 					}) | ||
|  | 
 | ||
|  | 				case ChangeOld: | ||
|  | 					b.Change = &BasicChange{ | ||
|  | 						Key:       line.Key, | ||
|  | 						Change:    line.Change, | ||
|  | 						Old:       line.Val, | ||
|  | 						LineStart: line.LineNum, | ||
|  | 					} | ||
|  | 
 | ||
|  | 				case ChangeNew: | ||
|  | 					b.Change.New = line.Val | ||
|  | 					b.Change.LineEnd = line.LineNum | ||
|  | 					b.Block.Changes = append(b.Block.Changes, b.Change) | ||
|  | 
 | ||
|  | 				default: | ||
|  | 					//ok
 | ||
|  | 				} | ||
|  | 
 | ||
|  | 			} else { | ||
|  | 				if line.Change != ChangeUnchanged { | ||
|  | 					if line.Key != "" { | ||
|  | 						b.narrow = line.Key | ||
|  | 						b.keysIdent = line.Indent | ||
|  | 					} | ||
|  | 
 | ||
|  | 					if line.Change != ChangeNil { | ||
|  | 						if !b.writing { | ||
|  | 							b.writing = true | ||
|  | 							key := b.Block.Title | ||
|  | 
 | ||
|  | 							if b.narrow != "" { | ||
|  | 								key = b.narrow | ||
|  | 								if b.keysIdent > line.Indent { | ||
|  | 									key = b.Block.Title | ||
|  | 								} | ||
|  | 							} | ||
|  | 
 | ||
|  | 							b.Summary = &BasicSummary{ | ||
|  | 								Key:       key, | ||
|  | 								Change:    line.Change, | ||
|  | 								LineStart: line.LineNum, | ||
|  | 							} | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} else { | ||
|  | 					if b.writing { | ||
|  | 						b.writing = false | ||
|  | 						b.Summary.LineEnd = line.LineNum | ||
|  | 						b.Block.Summaries = append(b.Block.Summaries, b.Summary) | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return blocks | ||
|  | } | ||
|  | 
 | ||
|  | // encStateMap is used in the template helper
 | ||
|  | var ( | ||
|  | 	encStateMap = map[ChangeType]string{ | ||
|  | 		ChangeAdded:   "added", | ||
|  | 		ChangeDeleted: "deleted", | ||
|  | 		ChangeOld:     "changed", | ||
|  | 		ChangeNew:     "changed", | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// tplFuncMap is the function map for each template
 | ||
|  | 	tplFuncMap = template.FuncMap{ | ||
|  | 		"getChange": func(c ChangeType) string { | ||
|  | 			state, ok := encStateMap[c] | ||
|  | 			if !ok { | ||
|  | 				return "changed" | ||
|  | 			} | ||
|  | 			return state | ||
|  | 		}, | ||
|  | 	} | ||
|  | ) | ||
|  | 
 | ||
|  | var ( | ||
|  | 	// tplBlock is the whole thing
 | ||
|  | 	tplBlock = `{{ define "block" -}} | ||
|  | {{ range . }} | ||
|  | <div class="diff-group"> | ||
|  | 	<div class="diff-block"> | ||
|  | 		<h2 class="diff-block-title"> | ||
|  | 			<i class="diff-circle diff-circle-{{ getChange .Change }} fa fa-circle"></i> | ||
|  | 			<strong class="diff-title">{{ .Title }}</strong> {{ getChange .Change }} | ||
|  | 		</h2> | ||
|  | 
 | ||
|  | 
 | ||
|  | 		<!-- Overview --> | ||
|  | 		{{ if .Old }} | ||
|  | 			<div class="change list-change diff-label">{{ .Old }}</div> | ||
|  | 			<i class="diff-arrow fa fa-long-arrow-right"></i> | ||
|  | 		{{ end }} | ||
|  | 		{{ if .New }} | ||
|  | 				<div class="change list-change diff-label">{{ .New }}</div> | ||
|  | 		{{ end }} | ||
|  | 
 | ||
|  | 		{{ if .LineStart }} | ||
|  | 			<diff-link-json | ||
|  | 				line-link="{{ .LineStart }}" | ||
|  | 				line-display="{{ .LineStart }}{{ if .LineEnd }} - {{ .LineEnd }}{{ end }}" | ||
|  | 				switch-view="ctrl.getDiff('html')" | ||
|  | 			/> | ||
|  | 		{{ end }} | ||
|  | 
 | ||
|  | 	</div> | ||
|  | 
 | ||
|  | 	<!-- Basic Changes --> | ||
|  | 	{{ range .Changes }} | ||
|  | 		<ul class="diff-change-container"> | ||
|  | 		{{ template "change" . }} | ||
|  | 		</ul> | ||
|  | 	{{ end }} | ||
|  | 
 | ||
|  | 	<!-- Basic Summary --> | ||
|  | 	{{ range .Summaries }} | ||
|  | 		{{ template "summary" . }} | ||
|  | 	{{ end }} | ||
|  | 
 | ||
|  | </div> | ||
|  | {{ end }} | ||
|  | {{ end }}` | ||
|  | 
 | ||
|  | 	// tplChange is the template for changes
 | ||
|  | 	tplChange = `{{ define "change" -}} | ||
|  | <li class="diff-change-group"> | ||
|  | 	<span class="bullet-position-container"> | ||
|  | 		<div class="diff-change-item diff-change-title">{{ getChange .Change }} {{ .Key }}</div> | ||
|  | 
 | ||
|  | 		<div class="diff-change-item"> | ||
|  | 			{{ if .Old }} | ||
|  | 				<div class="change list-change diff-label">{{ .Old }}</div> | ||
|  | 				<i class="diff-arrow fa fa-long-arrow-right"></i> | ||
|  | 			{{ end }} | ||
|  | 			{{ if .New }} | ||
|  | 					<div class="change list-change diff-label">{{ .New }}</div> | ||
|  | 			{{ end }} | ||
|  | 		</div> | ||
|  | 
 | ||
|  | 		{{ if .LineStart }} | ||
|  | 			<diff-link-json | ||
|  | 				line-link="{{ .LineStart }}" | ||
|  | 				line-display="{{ .LineStart }}{{ if .LineEnd }} - {{ .LineEnd }}{{ end }}" | ||
|  | 				switch-view="ctrl.getDiff('html')" | ||
|  | 			/> | ||
|  | 		{{ end }} | ||
|  | 	</span> | ||
|  | </li> | ||
|  | {{ end }}` | ||
|  | 
 | ||
|  | 	// tplSummary is for basis summaries
 | ||
|  | 	tplSummary = `{{ define "summary" -}} | ||
|  | <div class="diff-group-name"> | ||
|  | 	<i class="diff-circle diff-circle-{{ getChange .Change }} fa fa-circle-o diff-list-circle"></i> | ||
|  | 
 | ||
|  | 	{{ if .Count }} | ||
|  | 		<strong>{{ .Count }}</strong> | ||
|  | 	{{ end }} | ||
|  | 
 | ||
|  | 	{{ if .Key }} | ||
|  | 		<strong class="diff-summary-key">{{ .Key }}</strong> | ||
|  | 		{{ getChange .Change }} | ||
|  | 	{{ end }} | ||
|  | 
 | ||
|  | 	{{ if .LineStart }} | ||
|  | 		<diff-link-json | ||
|  | 			line-link="{{ .LineStart }}" | ||
|  | 			line-display="{{ .LineStart }}{{ if .LineEnd }} - {{ .LineEnd }}{{ end }}" | ||
|  | 			switch-view="ctrl.getDiff('html')" | ||
|  | 		/> | ||
|  | 	{{ end }} | ||
|  | </div> | ||
|  | {{ end }}` | ||
|  | ) |