forked from newrelic/go-agent
/
main.go
112 lines (103 loc) · 3.45 KB
/
main.go
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("}")
}