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  }