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