github.com/alkemics/goflow@v0.2.1/wrappers/types/wrappers.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/alkemics/goflow"
     8  )
     9  
    10  func Wrapper(_ func(interface{}) error, graph goflow.GraphRenderer) (goflow.GraphRenderer, error) {
    11  	// By first sorting the nodes by execution order, we make sure that
    12  	// the output types used to compute an input type have already been
    13  	// resolved and are therefore set properly.
    14  	wrappedNodes := sortNodesByExecutionOrder(graph.Nodes())
    15  
    16  	possibleTypes := make(map[string][]string)
    17  	varType := make(map[string]string)
    18  
    19  	// Start by storing the outputs: they hold the truth
    20  	for _, n := range wrappedNodes {
    21  		for _, field := range n.Outputs() {
    22  			if field.Type != "" && !strings.HasPrefix(field.Type, "?") && !strings.HasPrefix(field.Type, "@") {
    23  				varType[field.Name] = field.Type
    24  			} else {
    25  				possibleTypes[field.Name] = append(possibleTypes[field.Name], field.Type)
    26  			}
    27  		}
    28  	}
    29  
    30  	for _, n := range wrappedNodes {
    31  		for _, field := range n.Inputs() {
    32  			if typ, ok := varType[field.Name]; ok {
    33  				possibleTypes[field.Name] = append(possibleTypes[field.Name], typ)
    34  			} else {
    35  				possibleTypes[field.Name] = append(possibleTypes[field.Name], field.Type)
    36  			}
    37  		}
    38  	}
    39  
    40  	// Continue running while we reduce the number of errors.
    41  	iter := 0
    42  	changed := true
    43  	for changed {
    44  		iter++
    45  		if iter > 100 {
    46  			return nil, fmt.Errorf(
    47  				"reached type resolution iteration %d, there probably is an issue with the resolver",
    48  				iter,
    49  			)
    50  		}
    51  
    52  		possibleTypes = reduceTypes(possibleTypes)
    53  
    54  		before := len(varType)
    55  		for v, typs := range possibleTypes {
    56  			if _, ok := varType[v]; ok {
    57  				continue
    58  			}
    59  
    60  			combined := combineTypes(typs)
    61  			if len(combined) == 1 && isTypeResolved(combined[0]) {
    62  				varType[v] = combined[0]
    63  			}
    64  		}
    65  
    66  		changed = before < len(varType)
    67  	}
    68  
    69  	missings := make(map[string][]string)
    70  	for k, v := range possibleTypes {
    71  		if _, ok := varType[k]; !ok {
    72  			missings[k] = v
    73  		}
    74  	}
    75  	if len(missings) > 0 {
    76  		return graph, goflow.MultiError{
    77  			Errs: craftResolutionErrors(missings),
    78  		}
    79  	}
    80  
    81  	for k, n := range wrappedNodes {
    82  		inputs := n.Inputs()
    83  		for i := range inputs {
    84  			field := inputs[i]
    85  			if typ, ok := varType[field.Name]; ok {
    86  				field.Type = typ
    87  			}
    88  			inputs[i] = field
    89  		}
    90  
    91  		outputs := n.Outputs()
    92  		for i := range outputs {
    93  			field := outputs[i]
    94  			if typ, ok := varType[field.Name]; ok {
    95  				field.Type = typ
    96  			}
    97  			outputs[i] = field
    98  		}
    99  
   100  		wrappedNodes[k] = nodeRenderer{
   101  			NodeRenderer: n,
   102  			typedInputs:  inputs,
   103  			typedOutputs: outputs,
   104  		}
   105  	}
   106  
   107  	return graphRenderer{
   108  		GraphRenderer: graph,
   109  
   110  		nodes:     wrappedNodes,
   111  		outputMap: varType,
   112  	}, nil
   113  }