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 }