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  }