github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/scripts/confix/condiff/condiff.go (about) 1 // Program condiff performs a keyspace diff on two TOML documents. 2 package main 3 4 import ( 5 "context" 6 "flag" 7 "fmt" 8 "io" 9 "log" 10 "os" 11 "path/filepath" 12 "sort" 13 "strings" 14 15 "github.com/creachadair/tomledit" 16 "github.com/creachadair/tomledit/parser" 17 "github.com/creachadair/tomledit/transform" 18 ) 19 20 var ( 21 doDesnake = flag.Bool("desnake", false, "Convert snake_case to kebab-case before comparing") 22 ) 23 24 func init() { 25 flag.Usage = func() { 26 fmt.Fprintf(os.Stderr, `Usage: %[1]s [options] f1 f2 27 28 Diff the keyspaces of the TOML documents in files f1 and f2. 29 The output prints one line per key that differs: 30 31 -S name -- section exists in f1 but not f2 32 +S name -- section exists in f2 but not f1 33 -M name -- mapping exists in f1 but not f2 34 +M name -- mapping exists in f2 but not f1 35 36 Comments, order, and values are ignored for comparison purposes. 37 38 Options: 39 `, filepath.Base(os.Args[0])) 40 flag.PrintDefaults() 41 } 42 } 43 44 func main() { 45 flag.Parse() 46 47 if flag.NArg() != 2 { 48 log.Fatalf("Usage: %[1]s <lhs> <rhs>", filepath.Base(os.Args[0])) 49 } 50 lhs := mustParse(flag.Arg(0)) 51 rhs := mustParse(flag.Arg(1)) 52 if *doDesnake { 53 log.Printf("Converting all names from snake_case to kebab-case") 54 fix := transform.SnakeToKebab() 55 _ = fix(context.Background(), lhs) 56 _ = fix(context.Background(), rhs) 57 } 58 diffDocs(os.Stdout, lhs, rhs) 59 } 60 61 func mustParse(path string) *tomledit.Document { 62 f, err := os.Open(path) 63 if err != nil { 64 log.Fatalf("Opening TOML input: %v", err) 65 } 66 defer f.Close() 67 doc, err := tomledit.Parse(f) 68 if err != nil { 69 log.Fatalf("Parsing %q: %v", path, err) 70 } 71 return doc 72 } 73 74 func allKeys(s *tomledit.Section) []string { 75 var keys []string 76 s.Scan(func(key parser.Key, _ *tomledit.Entry) bool { 77 keys = append(keys, key.String()) 78 return true 79 }) 80 return keys 81 } 82 83 const ( 84 delSection = "-S" 85 delMapping = "-M" 86 addSection = "+S" 87 addMapping = "+M" 88 89 delMapSep = "\n" + delMapping + " " 90 addMapSep = "\n" + addMapping + " " 91 ) 92 93 func diffDocs(w io.Writer, lhs, rhs *tomledit.Document) { 94 diffSections(w, lhs.Global, rhs.Global) 95 lsec, rsec := lhs.Sections, rhs.Sections 96 transform.SortSectionsByName(lsec) 97 transform.SortSectionsByName(rsec) 98 99 i, j := 0, 0 100 for i < len(lsec) && j < len(rsec) { 101 if lsec[i].Name.Before(rsec[j].Name) { 102 fmt.Fprintln(w, delSection, lsec[i].Name) 103 fmt.Fprintln(w, delMapping, strings.Join(allKeys(lsec[i]), delMapSep)) 104 i++ 105 } else if rsec[j].Name.Before(lsec[i].Name) { 106 fmt.Fprintln(w, addSection, rsec[j].Name) 107 fmt.Fprintln(w, addMapping, strings.Join(allKeys(rsec[j]), addMapSep)) 108 j++ 109 } else { 110 diffSections(w, lsec[i], rsec[j]) 111 i++ 112 j++ 113 } 114 } 115 for ; i < len(lsec); i++ { 116 fmt.Fprintln(w, delSection, lsec[i].Name) 117 fmt.Fprintln(w, delMapping, strings.Join(allKeys(lsec[i]), delMapSep)) 118 } 119 for ; j < len(rsec); j++ { 120 fmt.Fprintln(w, addSection, rsec[j].Name) 121 fmt.Fprintln(w, addMapping, strings.Join(allKeys(rsec[j]), addMapSep)) 122 } 123 } 124 125 func diffSections(w io.Writer, lhs, rhs *tomledit.Section) { 126 diffKeys(w, allKeys(lhs), allKeys(rhs)) 127 } 128 129 func diffKeys(w io.Writer, lhs, rhs []string) { 130 sort.Strings(lhs) 131 sort.Strings(rhs) 132 133 i, j := 0, 0 134 for i < len(lhs) && j < len(rhs) { 135 if lhs[i] < rhs[j] { 136 fmt.Fprintln(w, delMapping, lhs[i]) 137 i++ 138 } else if lhs[i] > rhs[j] { 139 fmt.Fprintln(w, addMapping, rhs[j]) 140 j++ 141 } else { 142 i++ 143 j++ 144 } 145 } 146 for ; i < len(lhs); i++ { 147 fmt.Fprintln(w, delMapping, lhs[i]) 148 } 149 for ; j < len(rhs); j++ { 150 fmt.Fprintln(w, addMapping, rhs[j]) 151 } 152 }