package main import ( "fmt" "github.com/goccy/go-yaml" "github.com/spf13/cobra" "os" "reflect" ) // hack to be able to compare slices and dictionires that cannot be put into a map. func strval(v any) string { return fmt.Sprintf("%v", v) } func subtract(yaml2 yaml.MapSlice, yaml1 yaml.MapSlice) yaml.MapSlice { res := make(yaml.MapSlice, 0) for _, item := range yaml2 { k := item.Key v2 := item.Value v1 := yaml1.ToMap()[k] switch { case v2 != nil && v1 == nil: res = append(res, item) case reflect.DeepEqual(v1, v2): // delete is implicit by not copying to the output case reflect.TypeOf(v1) == reflect.TypeOf(yaml.MapSlice{}) && reflect.TypeOf(v2) == reflect.TypeOf(yaml.MapSlice{}): diff := subtract(v2.(yaml.MapSlice), v1.(yaml.MapSlice)) if len(diff) > 0 { mi := yaml.MapItem{Key: k, Value: diff} res = append(res, mi) } case Type(v1) == Slice && Type(v2) == Slice: // To be improved can be really confusing. v2set := make(map[string]bool) v1set := make(map[string]bool) stringToValue := make(map[any]any) for _, v := range v2.([]any) { sval := strval(v) stringToValue[sval] = v v2set[sval] = true } for _, v := range v1.([]any) { v1set[strval(v)] = true } s := make([]any, 0) for k2, _ := range v2set { if v1set[k2] { if VERBOSITY == 2 { s = append(s, "") } else if VERBOSITY == 3 { s = append(s, stringToValue[k2]) } } else { s = append(s, stringToValue[k2]) } } res = append(res, yaml.MapItem{ Key: k, Value: s, }) default: res = append(res, item) } } return res } func diff(cmd *cobra.Command, args []string) error { if len(args) != 2 { return fmt.Errorf("Expected 2 files") } if VERBOSITY < 0 || VERBOSITY > 3 { return fmt.Errorf("Array verbosity out of range") } file1 := args[0] file2 := args[1] yaml1 := parse(read(file1)) yaml2 := parse(read(file2)) diff1 := subtract(yaml2, yaml1) diff2 := make(yaml.MapSlice, 0) if SYMMETRIC_DIFF { diff2 = subtract(yaml1, yaml2) } diff := diff1 if SYMMETRIC_DIFF { diff = make(yaml.MapSlice, 0) diff = append(diff, yaml.MapItem{Key: "forward", Value: diff1}, yaml.MapItem{Key: "backward", Value: diff2}, ) } if VERBOSITY > 0 { if err := encode(os.Stdout, diff); err != nil { return err } } if len(diff1) > 0 || len(diff2) > 0 { os.Exit(1) } return nil }