github.com/newrelic/go-agent@v3.26.0+incompatible/internal/tools/interface-wrapping/main.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package main 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "os" 11 ) 12 13 // This program is generates code for wrapping interfaces which implement 14 // optional interfaces. For some context on the problem this solves, read: 15 // https://blog.merovius.de/2017/07/30/the-trouble-with-optional-interfaces.html 16 17 // This problem takes one of the json files in this directory as input: 18 // eg. go run main.go transaction_response_writer.json 19 20 func main() { 21 if len(os.Args) < 2 { 22 fmt.Println("provide input file") 23 os.Exit(1) 24 } 25 filename := os.Args[1] 26 inputBytes, err := ioutil.ReadFile(filename) 27 if nil != err { 28 fmt.Println(fmt.Errorf("unable to read %v: %v", filename, err)) 29 os.Exit(1) 30 } 31 32 var input struct { 33 // variableName must implement all of the required interfaces 34 // and all of the optional interfaces. It will be used to 35 // populate the fields of anonymous structs which have 36 // interfaces embedded. 37 VariableName string `json:"variable_name"` 38 // variableName is the variable that will be tested against the 39 // optional interfaces. It is the "thing being wrapped" whose 40 // behavior we seek to emulate. 41 TestVariableName string `json:"test_variable_name"` 42 RequiresInterfaces []string `json:"required_interfaces"` 43 OptionalInterfaces []string `json:"optional_interfaces"` 44 } 45 46 err = json.Unmarshal(inputBytes, &input) 47 if nil != err { 48 fmt.Println(fmt.Errorf("unable to unmarshal input: %v", err)) 49 os.Exit(1) 50 } 51 52 bitflagVariables := make([]string, len(input.OptionalInterfaces)) 53 for idx := range input.OptionalInterfaces { 54 bitflagVariables[idx] = fmt.Sprintf("i%d", idx) 55 } 56 57 fmt.Println("// GENERATED CODE DO NOT MODIFY") 58 fmt.Println("// This code generated by internal/tools/interface-wrapping") 59 fmt.Println("var (") 60 for idx := range input.OptionalInterfaces { 61 fmt.Println(fmt.Sprintf("%s int32 = 1 << %d", bitflagVariables[idx], idx)) 62 } 63 fmt.Println(")") 64 // interfaceSet is a bitset whose value represents the optional 65 // interfaces that $input.TestVariableName implements. 66 fmt.Println("var interfaceSet int32") 67 for idx, inter := range input.OptionalInterfaces { 68 fmt.Println(fmt.Sprintf("if _, ok := %s.(%s); ok {", input.TestVariableName, inter)) 69 fmt.Println(fmt.Sprintf("interfaceSet |= %s", bitflagVariables[idx])) 70 fmt.Println("}") 71 } 72 permutations := make([][]int, 1<<uint32(len(input.OptionalInterfaces))) 73 for permutationNumber := range permutations { 74 for idx := range input.OptionalInterfaces { 75 if 0 != (permutationNumber & (1 << uint32(idx))) { 76 permutations[permutationNumber] = append(permutations[permutationNumber], idx) 77 } 78 } 79 } 80 fmt.Println("switch interfaceSet {") 81 for _, permutation := range permutations { 82 var cs string 83 for i, elem := range permutation { 84 if i > 0 { 85 cs += " | " 86 } 87 cs += bitflagVariables[elem] 88 } 89 if cs == "" { 90 fmt.Println("default: // No optional interfaces implemented") 91 } else { 92 fmt.Println(fmt.Sprintf("case %s:", cs)) 93 } 94 fmt.Println("return struct {") 95 for _, required := range input.RequiresInterfaces { 96 fmt.Println(required) 97 } 98 for _, elem := range permutation { 99 fmt.Println(input.OptionalInterfaces[elem]) 100 } 101 totalImplements := len(input.RequiresInterfaces) + len(permutation) 102 var varList string 103 for i := 0; i < totalImplements; i++ { 104 if i > 0 { 105 varList += ", " 106 } 107 varList += input.VariableName 108 } 109 fmt.Println("} { " + varList + " }") 110 } 111 fmt.Println("}") 112 }