1//+build ignore 2 3// types_generate.go is meant to run with go generate. It will use 4// go/{importer,types} to track down all the RR struct types. Then for each type 5// it will generate conversion tables (TypeToRR and TypeToString) and banal 6// methods (len, Header, copy) based on the struct tags. The generated source is 7// written to ztypes.go, and is meant to be checked into git. 8package main 9 10import ( 11 "bytes" 12 "fmt" 13 "go/format" 14 "go/types" 15 "log" 16 "os" 17 18 "golang.org/x/tools/go/packages" 19) 20 21var packageHdr = ` 22// Code generated by "go run duplicate_generate.go"; DO NOT EDIT. 23 24package dns 25 26` 27 28func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) { 29 st, ok := t.Underlying().(*types.Struct) 30 if !ok { 31 return nil, false 32 } 33 if st.NumFields() == 0 { 34 return nil, false 35 } 36 if st.Field(0).Type() == scope.Lookup("RR_Header").Type() { 37 return st, false 38 } 39 if st.Field(0).Anonymous() { 40 st, _ := getTypeStruct(st.Field(0).Type(), scope) 41 return st, true 42 } 43 return nil, false 44} 45 46// loadModule retrieves package description for a given module. 47func loadModule(name string) (*types.Package, error) { 48 conf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo} 49 pkgs, err := packages.Load(&conf, name) 50 if err != nil { 51 return nil, err 52 } 53 return pkgs[0].Types, nil 54} 55 56func main() { 57 // Import and type-check the package 58 pkg, err := loadModule("github.com/miekg/dns") 59 fatalIfErr(err) 60 scope := pkg.Scope() 61 62 // Collect actual types (*X) 63 var namedTypes []string 64 for _, name := range scope.Names() { 65 o := scope.Lookup(name) 66 if o == nil || !o.Exported() { 67 continue 68 } 69 70 if st, _ := getTypeStruct(o.Type(), scope); st == nil { 71 continue 72 } 73 74 if name == "PrivateRR" || name == "OPT" { 75 continue 76 } 77 78 namedTypes = append(namedTypes, o.Name()) 79 } 80 81 b := &bytes.Buffer{} 82 b.WriteString(packageHdr) 83 84 // Generate the duplicate check for each type. 85 fmt.Fprint(b, "// isDuplicate() functions\n\n") 86 for _, name := range namedTypes { 87 88 o := scope.Lookup(name) 89 st, _ := getTypeStruct(o.Type(), scope) 90 fmt.Fprintf(b, "func (r1 *%s) isDuplicate(_r2 RR) bool {\n", name) 91 fmt.Fprintf(b, "r2, ok := _r2.(*%s)\n", name) 92 fmt.Fprint(b, "if !ok { return false }\n") 93 fmt.Fprint(b, "_ = r2\n") 94 for i := 1; i < st.NumFields(); i++ { 95 field := st.Field(i).Name() 96 o2 := func(s string) { fmt.Fprintf(b, s+"\n", field, field) } 97 o3 := func(s string) { fmt.Fprintf(b, s+"\n", field, field, field) } 98 99 // For some reason, a and aaaa don't pop up as *types.Slice here (mostly like because the are 100 // *indirectly* defined as a slice in the net package). 101 if _, ok := st.Field(i).Type().(*types.Slice); ok { 102 o2("if len(r1.%s) != len(r2.%s) {\nreturn false\n}") 103 104 if st.Tag(i) == `dns:"cdomain-name"` || st.Tag(i) == `dns:"domain-name"` { 105 o3(`for i := 0; i < len(r1.%s); i++ { 106 if !isDuplicateName(r1.%s[i], r2.%s[i]) { 107 return false 108 } 109 }`) 110 111 continue 112 } 113 114 if st.Tag(i) == `dns:"apl"` { 115 o3(`for i := 0; i < len(r1.%s); i++ { 116 if !r1.%s[i].equals(&r2.%s[i]) { 117 return false 118 } 119 }`) 120 121 continue 122 } 123 124 if st.Tag(i) == `dns:"pairs"` { 125 o2(`if !areSVCBPairArraysEqual(r1.%s, r2.%s) { 126 return false 127 }`) 128 129 continue 130 } 131 132 o3(`for i := 0; i < len(r1.%s); i++ { 133 if r1.%s[i] != r2.%s[i] { 134 return false 135 } 136 }`) 137 138 continue 139 } 140 141 switch st.Tag(i) { 142 case `dns:"-"`: 143 // ignored 144 case `dns:"a"`, `dns:"aaaa"`: 145 o2("if !r1.%s.Equal(r2.%s) {\nreturn false\n}") 146 case `dns:"cdomain-name"`, `dns:"domain-name"`: 147 o2("if !isDuplicateName(r1.%s, r2.%s) {\nreturn false\n}") 148 default: 149 o2("if r1.%s != r2.%s {\nreturn false\n}") 150 } 151 } 152 fmt.Fprintf(b, "return true\n}\n\n") 153 } 154 155 // gofmt 156 res, err := format.Source(b.Bytes()) 157 if err != nil { 158 b.WriteTo(os.Stderr) 159 log.Fatal(err) 160 } 161 162 // write result 163 f, err := os.Create("zduplicate.go") 164 fatalIfErr(err) 165 defer f.Close() 166 f.Write(res) 167} 168 169func fatalIfErr(err error) { 170 if err != nil { 171 log.Fatal(err) 172 } 173} 174