Skip to content

Commit

Permalink
BUG/MINOR: models: add more validation to diff method, support more a…
Browse files Browse the repository at this point in the history
…liases
  • Loading branch information
oktalz committed Aug 28, 2023
1 parent d2c1842 commit 5da134a
Show file tree
Hide file tree
Showing 87 changed files with 995 additions and 687 deletions.
1 change: 1 addition & 0 deletions cmd/struct_equal_generator/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ func generateEqualAndDiff(opt generateEqualAndDiffOptions) error {
"HasPrefix": strings.HasPrefix,
"Title": toTitle,
"CamelCase": toCamelCase,
"JSON": toJSON,
}
tmpl, err := template.New("generate.tmpl").Funcs(funcMaps).Parse(tmplEqualAndDiff)
if err != nil {
Expand Down
198 changes: 103 additions & 95 deletions cmd/struct_equal_generator/generate.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -27,63 +27,67 @@ func (s {{.Name}}) Equal(t {{.Name}}, opts ...Options) bool {
{{- if eq .Mode "struct" }}
{{- range .Fields}}
{{- if and (HasPrefix .Name "Index") (HasPrefix .Type "*int64") }}
if !opt.SkipIndex && !equalPointers(s.{{.Name}}, t.{{.Name}}){
return false
}
{{- else if or (eq .Type "string" "bool") (HasPrefix .Type "int") }}
if s.{{.Name}} != t.{{.Name}} {
return false
}
{{- else if or (HasPrefix .Type "[]int") (HasPrefix .Type "[]string")}}
if !equalComparableSlice(s.{{.Name}}, t.{{.Name}}, opt) {
return false
}
{{- else if or (HasPrefix .Type "*string") (HasPrefix .Type "*int") (HasPrefix .Type "*bool")}}
if !equalPointers(s.{{.Name}}, t.{{.Name}}) {
return false
}
{{- else if HasPrefix .Type "map" }}
if !equalComparableMap(s.{{.Name}}, t.{{.Name}}, opt) {
return false
}
{{- else if HasPrefix .Type "[]"}}
if !CheckSameNilAndLen(s.{{.Name}}, t.{{.Name}}, opt){
return false
}
for i := range s.{{.Name}} {
if !s.{{.Name}}[i].Equal({{if HasPrefix .Type "[]*"}}*{{end}}t.{{.Name}}[i], opt) {
if !opt.SkipIndex && !equalPointers(s.{{.Name}}, t.{{.Name}}){
return false
}
}
{{- else if .IsEmbedded}}
if !s.{{.Name}}.Equal({{if HasPrefix .Type "[]*"}}*{{end}}t.{{.Name}}, opt) {
return false
}
{{- else if (HasPrefix .Type "Date")}}
if !s.{{.Name}}.Equal(t.{{.Name}}) {
return false
}
{{- else if or (HasPrefix .Type "*") (not .IsBasicType)}}
{{- if or (eq .TypeInFile "time.Time") (eq .TypeInFile "strfmt.DateTime") }} {{/* time.Time has Equal, but nothing else */}}
if !s.{{.Name}}.Equal({{if HasPrefix .Type "*"}}*{{end}}t.{{.Name}}) {
return false
{{- else if or .HasEqual .HasEqualOpt }}
if !s.{{.Name}}.Equal({{if HasPrefix .Type "*"}}*{{end}}t.{{.Name}}{{if .HasEqualOpt}}, opt{{end}}) {
return false
}
{{- else if or .IsBasicType .IsComparable}}
{{- if HasPrefix .Type "*" }}
if !equalPointers(s.{{.Name}}, t.{{.Name}}) {
return false
}
{{- else }}
if !s.{{.Name}}.Equal({{if HasPrefix .Type "*"}}*{{end}}t.{{.Name}}, opt) {
if s.{{.Name}} != t.{{.Name}} {
return false
}
{{- end }}
{{- else if HasPrefix .Type "map" }}
if !equalComparableMap(s.{{.Name}}, t.{{.Name}}, opt) {
return false
}
{{- else if .IsArray }}
{{- if or .IsComparable .SubType.IsComparable }}
if !equalComparableSlice(s.{{.Name}}, t.{{.Name}}, opt) {
return false
}
{{- else }}
if !CheckSameNilAndLen(s.{{.Name}}, t.{{.Name}}, opt){
return false
}
for i := range s.{{.Name}} {
{{- if .SubType.HasEqual}}
if !s.{{.Name}}[i].Equal({{if HasPrefix .Type "[]*"}}*{{end}}t.{{.Name}}[i]) {
return false
}
{{- else if .SubType.HasEqualOpt}}
if !s.{{.Name}}[i].Equal({{if HasPrefix .Type "[]*"}}*{{end}}t.{{.Name}}[i], opt) {
return false
}
{{- else }}
{{- if HasPrefix .SubType.Type "*" }}
if !equalPointers(s.{{.Name}}, t.{{.Name}}) {
return false
}
{{- else }}
if s.{{.Name}}[i] != t.{{.Name}}[i] {
return false
}
{{- end }}
{{- end }}
}
{{- end }}
{{- else}}
if !s.{{.Name}}.Equal(&t.{{.Name}}, opt) {
return false
}
ERR // {{ . | JSON }}
{{end}}
{{end}}
return true
{{end}}
{{- if eq .Mode "array" }}
{{- if and .IsComparable .IsBasicType }}
return equalComparableSlice(s,t,opt)
return equalComparableSlice(s, t, opt)
{{- else }}
if !opt.NilSameAsEmpty {
if s == nil && t != nil {
Expand Down Expand Up @@ -148,62 +152,66 @@ func (s {{.Name}}) Diff(t {{.Name}}, opts ...Options) map[string][]interface{} {
diff := make(map[string][]interface{})
{{- range .Fields}}
{{- if and (HasPrefix .Name "Index") (HasPrefix .Type "*int64") }}
if !opt.SkipIndex && !equalPointers(s.{{.Name}}, t.{{.Name}}){
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else if or (eq .Type "string" "bool") (HasPrefix .Type "int") }}
if s.{{.Name}} != t.{{.Name}} {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else if or (HasPrefix .Type "[]int") (HasPrefix .Type "[]string")}}
if !equalComparableSlice(s.{{.Name}}, t.{{.Name}}, opt) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else if or (HasPrefix .Type "*string") (HasPrefix .Type "*int") (HasPrefix .Type "*bool")}}
if !equalPointers(s.{{.Name}}, t.{{.Name}}) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else if HasPrefix .Type "map" }}
if !equalComparableMap(s.{{.Name}}, t.{{.Name}}, opt) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else if HasPrefix .Type "[]"}}
if len(s.{{.Name}}) != len(t.{{.Name}}) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
} else {
diff2 := make(map[string][]interface{})
for i := range s.{{.Name}} {
diffSub := s.{{.Name}}[i].Diff({{if HasPrefix .Type "[]*"}}*{{end}}t.{{.Name}}[i], opt)
if len(diffSub) > 0 {
diff2[strconv.Itoa(i)] = []interface{}{ diffSub }
}
if !opt.SkipIndex && !equalPointers(s.{{.Name}}, t.{{.Name}}){
diff["{{.Name}}"] = []interface{}{ValueOrNil(s.{{.Name}}), ValueOrNil(t.{{.Name}})}
}
if len(diff2) > 0 {
diff["{{.Name}}"] = []interface{}{diff2}
{{- else if or .HasEqual .HasEqualOpt }}
if !s.{{.Name}}.Equal({{if HasPrefix .Type "*"}}*{{end}}t.{{.Name}}{{if .HasEqualOpt}}, opt{{end}}) {
diff["{{.Name}}"] = []interface{}{ {{if HasPrefix .Type "*"}}ValueOrNil({{end}}s.{{.Name}}{{if HasPrefix .Type "*"}}){{end}}, {{if HasPrefix .Type "*"}}ValueOrNil({{end}}t.{{.Name}}{{if HasPrefix .Type "*"}}){{end}} }
}
}
{{- else if .IsEmbedded}}
if !s.{{.Name}}.Equal(t.{{.Name}}, opt) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else if (HasPrefix .Type "Date")}}
if !s.{{.Name}}.Equal(t.{{.Name}}) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else if or (HasPrefix .Type "*") (not .IsBasicType)}}
{{- if or (eq .TypeInFile "time.Time") (eq .TypeInFile "strfmt.DateTime") }} {{/* time.Time has Equal, but nothing else */}}
if !s.{{.Name}}.Equal({{if HasPrefix .Type "*"}}*{{end}}t.{{.Name}}) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- else}}
if !s.{{.Name}}.Equal({{if HasPrefix .Type "*"}}*{{end}}t.{{.Name}}, opt) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
{{- else if or .IsBasicType .IsComparable}}
{{- if HasPrefix .Type "*" }}
if !equalPointers(s.{{.Name}}, t.{{.Name}}) {
diff["{{.Name}}"] = []interface{}{ ValueOrNil(s.{{.Name}}), ValueOrNil(t.{{.Name}})}
}
{{- else }}
if s.{{.Name}} != t.{{.Name}} {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
{{- end }}
{{- else if HasPrefix .Type "map" }}
if !equalComparableMap(s.{{.Name}}, t.{{.Name}}, opt) {
diff["{{.Name}}"] = []interface{}{ s.{{.Name}}, t.{{.Name}}}
}
{{- end}}
{{- else if .IsArray }}
{{- if .IsComparable }}
if !equalComparableSlice(s.{{.Name}}, t.{{.Name}}, opt) {
diff["{{.Name}}"] = []interface{}{ s.{{.Name}}, t.{{.Name}}}
}
{{- else }}
if !CheckSameNilAndLen(s.{{.Name}}, t.{{.Name}}, opt){
diff["{{.Name}}"] = []interface{}{ s.{{.Name}}, t.{{.Name}}}
} else {
diff2 := make(map[string][]interface{})
for i := range s.{{.Name}} {
{{- if .SubType.HasEqual}}
if !s.{{.Name}}[i].Equal({{if HasPrefix .Type "[]*"}}*{{end}}t.{{.Name}}[i]) {
diff2[strconv.Itoa(i)] = []interface{}{s.{{.Name}}[i], t.{{.Name}}[i] }
}
{{- else if .SubType.HasEqualOpt}}
diffSub := s.{{.Name}}[i].Diff({{if HasPrefix .Type "[]*"}}*{{end}}t.{{.Name}}[i], opt)
if len(diffSub) > 0 {
diff2[strconv.Itoa(i)] = []interface{}{ diffSub }
}
{{- else }}
{{- if HasPrefix .SubType.Type "*" }}
if !equalPointers(s.{{.Name}}, t.{{.Name}}) {
diff2[strconv.Itoa(i)] = []interface{}{ ValueOrNil(s.{{.Name}}[i]), ValueOrNil(t.{{.Name}}[i]) }
}
{{- else }}
if s.{{.Name}}[i] != t.{{.Name}}[i] {
diff2[strconv.Itoa(i)] = []interface{}{ s.{{.Name}}[i], t.{{.Name}}[i] }
}
{{- end }}
{{- end }}
}
if len(diff2) > 0 {
diff["{{.Name}}"] = []interface{}{diff2}
}
}
{{- end }}
{{- else}}
if !s.{{.Name}}.Equal(&t.{{.Name}}, opt) {
diff["{{.Name}}"] = []interface{}{s.{{.Name}}, t.{{.Name}}}
}
ERR // {{ . | JSON }}
{{end}}
{{end}}
{{- if eq .Mode "array" }}
Expand Down
90 changes: 80 additions & 10 deletions cmd/struct_equal_generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ func main() {
if err != nil {
log.Panic(err)
}
for _, fileName := range args.Files {
types := scanAllTypes(fileName)
for _, t := range types {
generatedTypes[t] = true
}
}
for _, fileName := range args.Files {
packageName, err = generate(fileName, args)
if err != nil {
Expand All @@ -30,18 +36,49 @@ func main() {
}
}

var sourceofFile string //nolint:gochecknoglobals
var sourceOfFile string //nolint:gochecknoglobals

func scanAllTypes(fileName string) []string {
fset := token.NewFileSet()

src, err := os.ReadFile(fileName)
if err != nil {
return []string{}
}
sourceOfFile = string(src)

// node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
node, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
return []string{}
}

typesInFile := []string{}
for _, f := range node.Decls {
g, ok := f.(*ast.GenDecl)
if !ok {
continue
}
for _, spec := range g.Specs {
currSpecType, ok := spec.(*ast.TypeSpec)
if !ok {
continue
}
typesInFile = append(typesInFile, currSpecType.Name.Name)
}
}
return typesInFile
}

//nolint:gocognit
func generate(fileName string, args Args) (string, error) {
func generate(fileName string, args Args) (string, error) { //nolint:gocognit,maintidx
fset := token.NewFileSet()
var packageName string

src, err := os.ReadFile(fileName)
if err != nil {
return packageName, err
}
sourceofFile = string(src)
sourceOfFile = string(src)

// node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
node, err := parser.ParseFile(fset, "", src, 0)
Expand Down Expand Up @@ -96,6 +133,15 @@ func generate(fileName string, args Args) (string, error) {
}

packageName = node.Name.String()
imports := map[string]string{}
for _, imp := range node.Imports {
if imp.Name != nil {
imports[imp.Name.Name] = strings.Trim(imp.Path.Value, "\"")
} else {
s := strings.Split(imp.Path.Value, "/")
imports[strings.Trim(s[len(s)-1], "\"")] = strings.Trim(imp.Path.Value, "\"")
}
}

hasTests := false
// For each declaration in the node's declarations list, check if it is a generic declaration.
Expand All @@ -119,14 +165,35 @@ func generate(fileName string, args Args) (string, error) {
needsOptionsIndex := false
for _, field := range currType.Fields.List {
if len(field.Names) > 0 {
res := getTypeString(field.Type)
fields = append(fields, Field{
res := getTypeString(field.Type, imports)
f := Field{
Name: field.Names[0].Name,
Type: res.Name,
TypeInFile: res.TypeName,
IsBasicType: res.IsBasicType,
IsComparable: res.IsComparable,
})
HasString: res.HasStringer,
HasEqual: res.HasEqual,
HasEqualOpt: res.HasEqualOpt,
IsArray: res.IsArray,
IsMap: res.IsMap,
}
if res.SubType != nil {
f.SubType = &Field{
Name: res.SubType.Name,
Type: res.SubType.Name,
TypeInFile: res.SubType.TypeName,
IsBasicType: res.SubType.IsBasicType,
IsComparable: res.SubType.IsComparable,
// IsEmbedded: res.SubType.IsEmbedded,
HasString: res.SubType.HasStringer,
HasEqual: res.SubType.HasEqual,
HasEqualOpt: res.SubType.HasEqualOpt,
IsArray: res.SubType.IsArray,
IsMap: res.SubType.IsMap,
}
}
fields = append(fields, f)
if field.Names[0].Name == "Index" {
needsOptionsIndex = true
}
Expand All @@ -140,11 +207,14 @@ func generate(fileName string, args Args) (string, error) {
}
// For embedded struct
if len(field.Names) == 0 && field.Type != nil {
res := getTypeString(field.Type)
res := getTypeString(field.Type, imports)
fields = append(fields, Field{
Name: res.Name,
IsEmbedded: true,
IsComparable: res.IsComparable,
HasString: res.HasStringer,
HasEqual: res.HasEqual,
HasEqualOpt: res.HasEqualOpt,
})
if res.Name == "Index" {
needsOptionsIndex = true
Expand Down Expand Up @@ -181,7 +251,7 @@ func generate(fileName string, args Args) (string, error) {
log.Panic(err)
}
case *ast.ArrayType:
res := getTypeString(currType.Elt)
res := getTypeString(currType.Elt, imports)
// needsOptions := !res.IsBasicType
needsOptions := true
needsOptionsIndex := false
Expand All @@ -197,7 +267,7 @@ func generate(fileName string, args Args) (string, error) {
CurrType: currSpecType,
IsBasicType: res.IsBasicType,
IsComplex: res.IsComplex,
IsComparable: res.IsComparable,
IsComparable: false,
IsPointer: strings.HasPrefix(res.Name, "*"),
NeedsOptions: needsOptions,
NeedsOptionsIndex: needsOptionsIndex,
Expand Down
Loading

0 comments on commit 5da134a

Please sign in to comment.