|
package main |
|
|
|
import ( |
|
"fmt" |
|
"io/ioutil" |
|
"log" |
|
"os" |
|
"regexp" |
|
"strings" |
|
) |
|
|
|
// FromTo is A little class that lets you check to see if two stored values are different |
|
type fromTo [2]string |
|
|
|
func createFromTo(from, to string) fromTo { |
|
return [2]string{from, to} |
|
} |
|
|
|
func (f *fromTo) getFrom() string { |
|
return f[0] |
|
} |
|
|
|
func (f *fromTo) getTo() string { |
|
return f[1] |
|
} |
|
|
|
func (f *fromTo) checkDifferent() bool { |
|
return f.getFrom() != f.getTo() |
|
} |
|
|
|
func (f *fromTo) getOneIfEmpty() string { |
|
// if we have different values, and one is empty, return the other |
|
if f.checkDifferent() { |
|
if f.getFrom() == "" { |
|
return f.getTo() |
|
} |
|
return f.getFrom() |
|
} |
|
|
|
return f.getFrom() |
|
} |
|
|
|
func (f *fromTo) setBothIgnoreEmpty(from, to string) { |
|
|
|
if f == nil { |
|
f = new(fromTo) |
|
} |
|
if from != "" { |
|
f[0] = from |
|
} |
|
|
|
if to != "" { |
|
f[1] = to |
|
} |
|
} |
|
|
|
var errlog *log.Logger |
|
|
|
func init() { |
|
var logFile, err = os.OpenFile("err.log", os.O_CREATE | os.O_WRONLY | os.O_TRUNC, 0644) |
|
if err != nil { |
|
panic(fmt.Sprintf("Couldn't open file err.log!!\n%s\n", err.Error())) |
|
} |
|
errlog = log.New(logFile, "tfeb> ", log.Ltime) |
|
|
|
|
|
} |
|
|
|
// You can put a little snippet here and uncomment the matching code in main to use this |
|
const test = `` |
|
|
|
type setting struct { |
|
Name *fromTo |
|
Namespace *fromTo |
|
Resource *fromTo |
|
Value *fromTo |
|
} |
|
|
|
func (s *setting) doDisplay() bool { |
|
return (s.Value != nil && s.Value.checkDifferent()) || (s.Resource != nil && s.Resource.checkDifferent()) |
|
} |
|
|
|
// Take key (name, namespace, etc) and populate the struct with it |
|
func (s *setting) populateWith(key string, val fromTo) { |
|
switch (key) { |
|
case "name": |
|
s.Name = new(fromTo) |
|
*s.Name = val |
|
break; |
|
case "namespace": |
|
s.Namespace = new(fromTo) |
|
*s.Namespace = val |
|
break; |
|
case "value": |
|
s.Value = new(fromTo) |
|
*s.Value = val |
|
break; |
|
case "resource": |
|
s.Resource = new(fromTo) |
|
*s.Resource = val |
|
break; |
|
} |
|
|
|
} |
|
|
|
// var valuePuller = regexp.MustCompile(`^setting.(\d+).(\w+):\s*"([\w ]*)" => "([\w ]*)"$`) |
|
var valuePuller = regexp.MustCompile(`\s*setting\.(\d+)\.(name|namespace|value|resource):\s*(?:"(.*)" => )?"(.*)"`) |
|
|
|
func main() { |
|
//stdin, err := ioutil.ReadAll(os.Stdin) |
|
if len(os.Args) != 2 { |
|
errlog.Println("Need a file") |
|
} |
|
|
|
stdin, err := ioutil.ReadFile(os.Args[1]) |
|
if err != nil { |
|
errlog.Println("no stdin!") |
|
os.Exit(-1) |
|
} |
|
lines := strings.Split(string(stdin), "\n") |
|
//NOTE: Change back when not testing |
|
//lines = strings.Split(test, "\n") |
|
|
|
settingMap := parseLinesIntoMap(lines) |
|
errlog.Println("~~~Now starting to parse out all the changes") |
|
diff := getChanges(settingMap) |
|
|
|
for k, v := range diff { |
|
fmt.Printf("%s: %s\n", k, v) |
|
} |
|
//for _, v := range settingMap { |
|
// if v != nil && v.doDisplay() { |
|
// if mBytes, err := json.MarshalIndent(v, "", " "); err == nil { |
|
// fmt.Println(string(mBytes)) |
|
// } else { |
|
// errlog.Println(mBytes) |
|
// } |
|
// } |
|
//} |
|
} |
|
|
|
type change struct { |
|
Name string |
|
Namespace string |
|
Value fromTo |
|
} |
|
|
|
type changes map[string]string |
|
|
|
func noneNil(pointers ...*fromTo) bool { |
|
for _, p := range pointers { |
|
if p == nil { |
|
return false |
|
} |
|
} |
|
return true |
|
} |
|
|
|
func getChanges(settings map[string]*setting) changes { |
|
|
|
// Map of Name,Namespace -> Value |
|
namesSpaceNameMap := make(map[[2]string]*fromTo) |
|
for _, v := range settings { |
|
if noneNil(v.Namespace, v.Name, v.Value) { |
|
key := [2]string{v.Name.getOneIfEmpty(), v.Namespace.getOneIfEmpty()} |
|
valueFT, ok := namesSpaceNameMap[key] |
|
if !ok || valueFT == nil { |
|
valueFT = new(fromTo) |
|
} |
|
valueFT.setBothIgnoreEmpty(v.Value.getFrom(), v.Value.getTo()) |
|
namesSpaceNameMap[key] = valueFT |
|
} |
|
} |
|
|
|
var result changes = make(changes) |
|
|
|
for k, v := range namesSpaceNameMap { |
|
// namespace/name = from -> to |
|
if v.checkDifferent() { |
|
result[fmt.Sprintf("%s/%s", k[1], k[0])] = fmt.Sprintf("%s -> %s", v.getFrom(), v.getTo()) |
|
} |
|
} |
|
|
|
return result |
|
} |
|
|
|
func parseLinesIntoMap(lines []string) map[string]*setting { |
|
settingMap := make(map[string]*setting) |
|
for _, line := range lines { |
|
matches := valuePuller.FindStringSubmatch(line) |
|
|
|
const printMat = "%-15s: [%s]\n" |
|
if matches == nil || len(matches) < 4 { |
|
errlog.Printf(printMat, "not a match", line) |
|
continue |
|
} else { |
|
errlog.Printf(printMat, "match!", line) |
|
} |
|
|
|
var found *setting |
|
if val, ok := settingMap[matches[1]]; ok && val != nil { |
|
found = val |
|
} else { |
|
found = new(setting) |
|
settingMap[matches[1]] = found |
|
} |
|
|
|
if len(matches) == 5 { |
|
found.populateWith(matches[2], createFromTo(matches[3], matches[4])) |
|
} else if len(matches) == 4 { |
|
found.populateWith(matches[2], createFromTo("", matches[3])) |
|
} else { |
|
errlog.Printf("incorrect number of matches: [%s]\n", line) |
|
continue |
|
} |
|
|
|
} |
|
return settingMap |
|
} |