github.com/avenga/couper@v1.12.2/errors/generate/types.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"regexp"
     9  
    10  	"github.com/avenga/couper/errors"
    11  )
    12  
    13  var couperErrors []string
    14  
    15  func main() {
    16  	println("generating error types:\n")
    17  
    18  	// hold list of possible base kinds which can be skipped for type definitions
    19  	b, err := os.ReadFile(filepath.Join("errors", "couper.go"))
    20  	must(err)
    21  	result := regexp.MustCompile(`\t(\w+)\s+= &Error.+\n`).FindAllStringSubmatch(string(b), -1)
    22  	for _, r := range result {
    23  		couperErrors = append(couperErrors, r[1])
    24  	}
    25  
    26  	errTypes := map[string]int{}
    27  	sortedErrTypes := make([]string, len(errors.Definitions))
    28  	for idx, errImpl := range errors.Definitions {
    29  		kinds := errImpl.Kinds()
    30  		if len(kinds) == 0 {
    31  			must(fmt.Errorf("error kind must be defined"))
    32  		}
    33  
    34  		kind := kinds[0]
    35  		if _, exist := errTypes[kind]; exist {
    36  			must(fmt.Errorf("error kind already defined: %s", kind))
    37  		}
    38  		errTypes[kind] = idx
    39  		sortedErrTypes[idx] = kind
    40  	}
    41  
    42  	for _, kind := range sortedErrTypes {
    43  		println("\t", kind, "-->", errors.SnakeToCamel(kind))
    44  	}
    45  
    46  	generated, err := os.Create(filepath.Join("errors", "types_generated.go"))
    47  	must(err)
    48  	defer generated.Close()
    49  
    50  	_, err = io.WriteString(generated, `// Code generated by go generate; DO NOT EDIT.
    51  
    52  package errors
    53  
    54  var (
    55  `)
    56  	must(err)
    57  
    58  	for idx, kind := range sortedErrTypes {
    59  		if isDefined(kind) {
    60  			continue
    61  		}
    62  		_, err = io.WriteString(generated, fmt.Sprintf("\t%s = Definitions[%d]\n", errors.SnakeToCamel(kind), idx))
    63  		must(err)
    64  	}
    65  
    66  	_, err = io.WriteString(generated, `)
    67  
    68  // typeDefinitions holds all related error definitions which are
    69  // catchable with an error_handler definition.
    70  type typeDefinitions map[string]*Error
    71  
    72  // types holds all implemented ones. The name must match the structs
    73  // snake-name for fallback purposes. See TypeToSnake usage and reference.
    74  var types = typeDefinitions{
    75  `)
    76  
    77  	must(err)
    78  
    79  	for _, kind := range sortedErrTypes {
    80  		_, err = io.WriteString(generated, fmt.Sprintf("\t%q: %s,\n", kind, errors.SnakeToCamel(kind)))
    81  		must(err)
    82  	}
    83  
    84  	_, err = io.WriteString(generated, `}
    85  
    86  // IsKnown tells the configuration callee if Couper
    87  // has a defined error type with the given name.
    88  func IsKnown(errorType string) bool {
    89  	_, known := types[errorType]
    90  	return known
    91  }
    92  
    93  `)
    94  	must(err)
    95  
    96  	var superKindsMapsByContext = make(map[string]map[string][]string)
    97  
    98  	for _, def := range errors.Definitions {
    99  		if def.IsParent() || def.Contexts == nil || len(def.Contexts) == 0 {
   100  			continue
   101  		}
   102  		// use only leaf types with context(s)
   103  		for _, context := range def.Contexts {
   104  			superKindsMaps, exists := superKindsMapsByContext[context]
   105  			if !exists {
   106  				superKindsMaps = make(map[string][]string)
   107  				superKindsMapsByContext[context] = superKindsMaps
   108  			}
   109  			kinds := append(def.Kinds(), "*")
   110  			kind := kinds[0]
   111  			kinds = kinds[1:]
   112  			for _, k := range kinds {
   113  				superKindsMaps[k] = append(superKindsMaps[k], kind)
   114  			}
   115  		}
   116  	}
   117  
   118  	_, err = io.WriteString(generated, `
   119  // SuperTypesMapsByContext holds maps for error super-types to sub-types
   120  // by a given context block type (e.g. api or endpoint).
   121  var SuperTypesMapsByContext = `)
   122  	must(err)
   123  	_, err = io.WriteString(generated, fmt.Sprintf("%#v\n", superKindsMapsByContext))
   124  	must(err)
   125  }
   126  
   127  // isDefined checks if the given typeName is defined and must be skipped for declaration with true result.
   128  func isDefined(typeName string) bool {
   129  	for _, t := range couperErrors {
   130  		if errors.SnakeToCamel(typeName) == t {
   131  			return true
   132  		}
   133  	}
   134  	return false
   135  }
   136  
   137  func must(err error) {
   138  	if err != nil {
   139  		println(err.Error())
   140  		os.Exit(1)
   141  	}
   142  }