github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/store/layer_generators/main.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package main 5 6 import ( 7 "bytes" 8 "fmt" 9 "go/ast" 10 "go/format" 11 "go/parser" 12 "go/token" 13 "io/ioutil" 14 "log" 15 "os" 16 "path" 17 "strings" 18 "text/template" 19 ) 20 21 const OPEN_TRACING_PARAMS_MARKER = "@openTracingParams" 22 23 func main() { 24 if err := buildTimerLayer(); err != nil { 25 log.Fatal(err) 26 } 27 if err := buildOpenTracingLayer(); err != nil { 28 log.Fatal(err) 29 } 30 } 31 32 func buildTimerLayer() error { 33 code, err := generateLayer("TimerLayer", "timer_layer.go.tmpl") 34 if err != nil { 35 return err 36 } 37 formatedCode, err := format.Source(code) 38 if err != nil { 39 return err 40 } 41 42 return ioutil.WriteFile(path.Join("timer_layer.go"), formatedCode, 0644) 43 } 44 45 func buildOpenTracingLayer() error { 46 code, err := generateLayer("OpenTracingLayer", "opentracing_layer.go.tmpl") 47 if err != nil { 48 return err 49 } 50 formatedCode, err := format.Source(code) 51 if err != nil { 52 return err 53 } 54 55 return ioutil.WriteFile(path.Join("opentracing_layer.go"), formatedCode, 0644) 56 } 57 58 type methodParam struct { 59 Name string 60 Type string 61 } 62 63 type methodData struct { 64 Params []methodParam 65 Results []string 66 ParamsToTrace map[string]bool 67 } 68 69 type subStore struct { 70 Methods map[string]methodData 71 } 72 73 type storeMetadata struct { 74 Name string 75 SubStores map[string]subStore 76 Methods map[string]methodData 77 } 78 79 func extractMethodMetadata(method *ast.Field, src []byte) methodData { 80 params := []methodParam{} 81 results := []string{} 82 paramsToTrace := map[string]bool{} 83 ast.Inspect(method.Type, func(expr ast.Node) bool { 84 switch e := expr.(type) { 85 case *ast.FuncType: 86 if method.Doc != nil { 87 for _, comment := range method.Doc.List { 88 s := comment.Text 89 if idx := strings.Index(s, OPEN_TRACING_PARAMS_MARKER); idx != -1 { 90 for _, p := range strings.Split(s[idx+len(OPEN_TRACING_PARAMS_MARKER):], ",") { 91 paramsToTrace[strings.TrimSpace(p)] = true 92 } 93 } 94 } 95 } 96 if e.Params != nil { 97 for _, param := range e.Params.List { 98 for _, paramName := range param.Names { 99 params = append(params, methodParam{Name: paramName.Name, Type: string(src[param.Type.Pos()-1 : param.Type.End()-1])}) 100 } 101 } 102 } 103 if e.Results != nil { 104 for _, result := range e.Results.List { 105 results = append(results, string(src[result.Type.Pos()-1:result.Type.End()-1])) 106 } 107 } 108 109 for paramName := range paramsToTrace { 110 found := false 111 for _, param := range params { 112 if param.Name == paramName { 113 found = true 114 break 115 } 116 } 117 if !found { 118 log.Fatalf("Unable to find a parameter called '%s' (method '%s') that is mentioned in the '%s' comment. Maybe it was renamed?", paramName, method.Names[0].Name, OPEN_TRACING_PARAMS_MARKER) 119 } 120 } 121 } 122 return true 123 }) 124 return methodData{Params: params, Results: results, ParamsToTrace: paramsToTrace} 125 } 126 127 func extractStoreMetadata() (*storeMetadata, error) { 128 // Create the AST by parsing src. 129 fset := token.NewFileSet() // positions are relative to fset 130 131 file, err := os.Open("store.go") 132 if err != nil { 133 return nil, fmt.Errorf("Unable to open store/store.go file: %w", err) 134 } 135 src, err := ioutil.ReadAll(file) 136 if err != nil { 137 return nil, err 138 } 139 file.Close() 140 f, err := parser.ParseFile(fset, "", src, parser.AllErrors|parser.ParseComments) 141 if err != nil { 142 return nil, err 143 } 144 145 topLevelFunctions := map[string]bool{ 146 "MarkSystemRanUnitTests": false, 147 "Close": false, 148 "LockToMaster": false, 149 "UnlockFromMaster": false, 150 "DropAllTables": false, 151 "TotalMasterDbConnections": true, 152 "TotalReadDbConnections": true, 153 "SetContext": true, 154 "TotalSearchDbConnections": true, 155 "GetCurrentSchemaVersion": true, 156 } 157 158 metadata := storeMetadata{Methods: map[string]methodData{}, SubStores: map[string]subStore{}} 159 160 ast.Inspect(f, func(n ast.Node) bool { 161 switch x := n.(type) { 162 case *ast.TypeSpec: 163 if x.Name.Name == "Store" { 164 for _, method := range x.Type.(*ast.InterfaceType).Methods.List { 165 methodName := method.Names[0].Name 166 if _, ok := topLevelFunctions[methodName]; ok { 167 metadata.Methods[methodName] = extractMethodMetadata(method, src) 168 } 169 } 170 } else if strings.HasSuffix(x.Name.Name, "Store") { 171 subStoreName := strings.TrimSuffix(x.Name.Name, "Store") 172 metadata.SubStores[subStoreName] = subStore{Methods: map[string]methodData{}} 173 for _, method := range x.Type.(*ast.InterfaceType).Methods.List { 174 methodName := method.Names[0].Name 175 metadata.SubStores[subStoreName].Methods[methodName] = extractMethodMetadata(method, src) 176 } 177 } 178 } 179 return true 180 }) 181 182 return &metadata, nil 183 } 184 185 func generateLayer(name, templateFile string) ([]byte, error) { 186 out := bytes.NewBufferString("") 187 metadata, err := extractStoreMetadata() 188 if err != nil { 189 return nil, err 190 } 191 metadata.Name = name 192 193 myFuncs := template.FuncMap{ 194 "joinResults": func(results []string) string { 195 return strings.Join(results, ", ") 196 }, 197 "joinResultsForSignature": func(results []string) string { 198 if len(results) == 0 { 199 return "" 200 } 201 if len(results) == 1 { 202 return strings.Join(results, ", ") 203 } 204 return fmt.Sprintf("(%s)", strings.Join(results, ", ")) 205 }, 206 "genResultsVars": func(results []string) string { 207 vars := []string{} 208 for i := range results { 209 vars = append(vars, fmt.Sprintf("resultVar%d", i)) 210 } 211 return strings.Join(vars, ", ") 212 }, 213 "errorToBoolean": func(results []string) string { 214 for i, typeName := range results { 215 if typeName == "*model.AppError" { 216 return fmt.Sprintf("resultVar%d == nil", i) 217 } 218 } 219 return "true" 220 }, 221 "errorPresent": func(results []string) bool { 222 for _, typeName := range results { 223 if typeName == "*model.AppError" { 224 return true 225 } 226 } 227 return false 228 }, 229 "errorVar": func(results []string) string { 230 for i, typeName := range results { 231 if typeName == "*model.AppError" { 232 return fmt.Sprintf("resultVar%d", i) 233 } 234 } 235 return "" 236 }, 237 "joinParams": func(params []methodParam) string { 238 paramsNames := make([]string, 0, len(params)) 239 for _, param := range params { 240 paramsNames = append(paramsNames, param.Name) 241 } 242 return strings.Join(paramsNames, ", ") 243 }, 244 "joinParamsWithType": func(params []methodParam) string { 245 paramsWithType := []string{} 246 for _, param := range params { 247 paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type)) 248 } 249 return strings.Join(paramsWithType, ", ") 250 }, 251 } 252 253 t := template.Must(template.New(templateFile).Funcs(myFuncs).ParseFiles("layer_generators/" + templateFile)) 254 if err = t.Execute(out, metadata); err != nil { 255 return nil, err 256 } 257 return out.Bytes(), nil 258 }