github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/diff/diff_sink.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package diff 16 17 import ( 18 "context" 19 "errors" 20 "io" 21 22 "github.com/fatih/color" 23 24 "github.com/dolthub/dolt/go/libraries/doltcore/row" 25 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 26 "github.com/dolthub/dolt/go/libraries/doltcore/table/pipeline" 27 "github.com/dolthub/dolt/go/libraries/doltcore/table/untyped" 28 "github.com/dolthub/dolt/go/libraries/doltcore/table/untyped/tabular" 29 "github.com/dolthub/dolt/go/store/types" 30 ) 31 32 const ( 33 colorRowProp = "color" 34 diffColTag = schema.ReservedTagMin 35 diffColName = "__diff__" 36 ) 37 38 type ColorFunc func(...interface{}) string 39 40 type ColorDiffSink struct { 41 sch schema.Schema 42 ttw *tabular.TextTableWriter 43 } 44 45 // NewColorDiffSink returns a ColorDiffSink that uses the writer and schema given to print its output. numHeaderRows 46 // will change how many rows of output are considered part of the table header. Use 1 for diffs where the schemas are 47 // the same between the two table revisions, and 2 for when they differ. 48 func NewColorDiffSink(wr io.WriteCloser, sch schema.Schema, numHeaderRows int) (*ColorDiffSink, error) { 49 _, additionalCols := untyped.NewUntypedSchemaWithFirstTag(diffColTag, diffColName) 50 outSch, err := untyped.UntypedSchemaUnion(additionalCols, sch) 51 if err != nil { 52 panic(err) 53 } 54 55 ttw, err := tabular.NewTextTableWriterWithNumHeaderRows(wr, outSch, numHeaderRows) 56 57 if err != nil { 58 return nil, err 59 } 60 61 return &ColorDiffSink{outSch, ttw}, nil 62 } 63 64 // GetSchema gets the schema of the rows that this writer writes 65 func (cds *ColorDiffSink) GetSchema() schema.Schema { 66 return cds.sch 67 } 68 69 var colDiffColors = map[DiffChType]ColorFunc{ 70 DiffAdded: color.New(color.Bold, color.FgGreen).Sprint, 71 DiffModifiedOld: color.New(color.FgRed).Sprint, 72 DiffModifiedNew: color.New(color.FgGreen).Sprint, 73 DiffRemoved: color.New(color.Bold, color.FgRed).Sprint, 74 } 75 76 func (cds *ColorDiffSink) ProcRowWithProps(r row.Row, props pipeline.ReadableMap) error { 77 78 taggedVals := make(row.TaggedValues) 79 allCols := cds.sch.GetAllCols() 80 colDiffs := make(map[string]DiffChType) 81 82 if prop, ok := props.Get(CollChangesProp); ok { 83 if convertedVal, convertedOK := prop.(map[string]DiffChType); convertedOK { 84 colDiffs = convertedVal 85 } 86 } 87 88 err := allCols.Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 89 if val, ok := r.GetColVal(tag); ok { 90 taggedVals[tag] = val.(types.String) 91 } 92 return false, nil 93 }) 94 95 if err != nil { 96 return err 97 } 98 99 taggedVals[diffColTag] = types.String(" ") 100 colorColumns := true 101 if prop, ok := props.Get(DiffTypeProp); ok { 102 if dt, convertedOK := prop.(DiffChType); convertedOK { 103 switch dt { 104 case DiffAdded: 105 taggedVals[diffColTag] = types.String(" + ") 106 colorColumns = false 107 case DiffRemoved: 108 taggedVals[diffColTag] = types.String(" - ") 109 colorColumns = false 110 case DiffModifiedOld: 111 taggedVals[diffColTag] = types.String(" < ") 112 case DiffModifiedNew: 113 taggedVals[diffColTag] = types.String(" > ") 114 } 115 // Treat the diff indicator string as a diff of the same type 116 colDiffs[diffColName] = dt 117 } 118 } 119 120 // Color the columns as appropriate. Some rows will be all colored. 121 err = allCols.Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 122 var colorFunc ColorFunc 123 if colorColumns { 124 if dt, ok := colDiffs[col.Name]; ok { 125 if fn, ok := colDiffColors[dt]; ok { 126 colorFunc = fn 127 } 128 } 129 } else { 130 if prop, ok := props.Get(DiffTypeProp); ok { 131 if dt, convertedOK := prop.(DiffChType); convertedOK { 132 if fn, ok := colDiffColors[dt]; ok { 133 colorFunc = fn 134 } 135 } 136 } 137 } 138 139 if colorFunc != nil { 140 taggedVals[tag] = types.String(colorFunc(string(taggedVals[tag].(types.String)))) 141 } 142 143 return false, nil 144 }) 145 146 if err != nil { 147 return err 148 } 149 150 r, err = row.New(r.Format(), cds.sch, taggedVals) 151 152 if err != nil { 153 return err 154 } 155 156 return cds.ttw.WriteRow(context.TODO(), r) 157 } 158 159 // Close should release resources being held 160 func (cds *ColorDiffSink) Close() error { 161 if cds.ttw != nil { 162 if err := cds.ttw.Close(context.TODO()); err != nil { 163 return err 164 } 165 cds.ttw = nil 166 return nil 167 } else { 168 return errors.New("Already closed.") 169 } 170 }