github.com/oam-dev/kubevela@v1.9.11/pkg/appfile/dryrun/report.go (about) 1 /* 2 Copyright 2021 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package dryrun 18 19 import ( 20 "fmt" 21 "io" 22 "math" 23 24 "github.com/aryann/difflib" 25 "github.com/fatih/color" 26 ) 27 28 var ( 29 red = color.New(color.FgRed) 30 green = color.New(color.FgGreen) 31 yellow = color.New(color.FgYellow) 32 white = color.New(color.FgWhite) 33 ) 34 35 // NewReportDiffOption creats a new ReportDiffOption that can formats and prints 36 // diff report into an io.Writer 37 func NewReportDiffOption(ctx int, to io.Writer) *ReportDiffOption { 38 return &ReportDiffOption{ 39 DiffMsgs: map[DiffType]string{ 40 AddDiff: "has been added(+)", 41 ModifyDiff: "has been modified(*)", 42 RemoveDiff: "has been removed(-)", 43 NoDiff: "has no change", 44 }, 45 Context: ctx, 46 To: to, 47 } 48 } 49 50 // ReportDiffOption contains options to formats and prints diff report 51 type ReportDiffOption struct { 52 DiffMsgs map[DiffType]string 53 Context int 54 To io.Writer 55 } 56 57 // PrintDiffReport formats and prints diff data into target io.Writer 58 func (r *ReportDiffOption) PrintDiffReport(diff *DiffEntry) { 59 r.printDiffReport(diff, "") 60 } 61 62 func (r *ReportDiffOption) printDiffReport(diff *DiffEntry, prefix string) { 63 var header string 64 switch diff.Kind { 65 case AppKind: 66 header = "Application" 67 case AppConfigCompKind: 68 case RawCompKind: 69 header = "Component" 70 case TraitKind: 71 header = "Trait" 72 case PolicyKind: 73 header = "External Policy" 74 case WorkflowKind: 75 header = "External Workflow" 76 case ReferredObject: 77 header = "Referred Object" 78 default: 79 return 80 } 81 if diff.Kind != AppConfigCompKind { 82 editMsg := r.DiffMsgs[diff.DiffType] 83 if diff.DiffType != NoDiff { 84 _, _ = yellow.Fprintf(r.To, "* %s%s (%s) %s\n", prefix, header, diff.Name, editMsg) 85 printDiffs(diff.Diffs, r.Context, r.To) 86 } else { 87 _, _ = white.Fprintf(r.To, "* %s%s (%s) %s\n", prefix, header, diff.Name, editMsg) 88 } 89 } 90 for _, sub := range diff.Subs { 91 var subPrefix string 92 if sub.Kind == TraitKind && diff.Kind == AppConfigCompKind { 93 subPrefix = fmt.Sprintf("Component (%s) / ", diff.Name) 94 } 95 r.printDiffReport(sub, subPrefix) 96 } 97 } 98 99 func printDiffs(diffs []difflib.DiffRecord, context int, to io.Writer) { 100 if context > 0 { 101 ctx := calculateContext(diffs) 102 skip := false 103 for i, diff := range diffs { 104 if ctx[i] <= context { 105 // only print the line whose distance to a closest diff is less 106 // than context 107 printDiffRecord(to, diff) 108 skip = false 109 } else if !skip { 110 fmt.Fprint(to, "...\n") 111 // skip print if next line is still omitted 112 skip = true 113 } 114 115 } 116 } else { 117 for _, diff := range diffs { 118 printDiffRecord(to, diff) 119 } 120 } 121 } 122 123 // calculateContext calculate the min distance from each line to its closest diff 124 func calculateContext(diffs []difflib.DiffRecord) map[int]int { 125 ctx := map[int]int{} 126 // retrieve forward to calculate the min distance from each line to a 127 // changed line behind it 128 changeLineNum := -1 129 for i, diff := range diffs { 130 if diff.Delta != difflib.Common { 131 changeLineNum = i 132 } 133 distance := math.MaxInt32 134 if changeLineNum != -1 { 135 distance = i - changeLineNum 136 } 137 ctx[i] = distance 138 } 139 // retrieve backward to calculate the min distance from each line to a 140 // changed line before it 141 changeLineNum = -1 142 for i := len(diffs) - 1; i >= 0; i-- { 143 if diffs[i].Delta != difflib.Common { 144 changeLineNum = i 145 } 146 if changeLineNum != -1 { 147 distance := changeLineNum - i 148 if distance < ctx[i] { 149 ctx[i] = distance 150 } 151 } 152 } 153 return ctx 154 } 155 156 func printDiffRecord(to io.Writer, diff difflib.DiffRecord) { 157 data := diff.Payload 158 switch diff.Delta { 159 case difflib.RightOnly: 160 _, _ = green.Fprintf(to, "+ %s\n", data) 161 case difflib.LeftOnly: 162 _, _ = red.Fprintf(to, "- %s\n", data) 163 case difflib.Common: 164 _, _ = fmt.Fprintf(to, " %s\n", data) 165 } 166 }