github.com/alkemics/goflow@v0.2.1/wrapper.go (about) 1 package goflow 2 3 // The GraphWrapper function method is the main building block of GoFlow. It is 4 // used to add features to goflow, such as inspection or keywords. 5 // 6 // Implementing a GraphWrapper might not always be the best fit. If you need something 7 // simpler, make sure you have a look at the other blocks: 8 // - NodeWrapper 9 // - Linter 10 // - Checker 11 // goflow provides functions to use those simpler types as GraphWrappers. 12 type GraphWrapper func(unmarshal func(interface{}) error, graph GraphRenderer) (GraphRenderer, error) 13 14 // The NodeWrapper should be used when no operation needs to be done on 15 // the graph itself, i.e. when everything is handled at the node level. 16 // 17 // To register a NodeWrapper in the builder, use FromNodeWrapper. 18 type NodeWrapper func(unmarshal func(interface{}) error, node NodeRenderer) (NodeRenderer, error) 19 20 type nodeWrapperGraphRenderer struct { 21 GraphRenderer 22 23 nodes []NodeRenderer 24 } 25 26 func (g nodeWrapperGraphRenderer) Nodes() []NodeRenderer { 27 return g.nodes 28 } 29 30 func (g nodeWrapperGraphRenderer) Imports() []Import { 31 imports := g.GraphRenderer.Imports() 32 33 for _, node := range g.Nodes() { 34 imports = append(imports, node.Imports()...) 35 } 36 37 return imports 38 } 39 40 func fakeUnmarshal(interface{}) error { return nil } 41 42 // FromNodeWrapper converts a NodeWrapper into a GraphWrapper. 43 func FromNodeWrapper(nw NodeWrapper) GraphWrapper { 44 return func(unmarshal func(interface{}) error, graph GraphRenderer) (GraphRenderer, error) { 45 var nodes struct { 46 Nodes []nodeLoader 47 } 48 if err := unmarshal(&nodes); err != nil { 49 return nil, err 50 } 51 52 ns := graph.Nodes() 53 errs := make([]error, 0, len(ns)) 54 wrappedNodes := make([]NodeRenderer, len(ns)) 55 copy(wrappedNodes, ns) 56 57 for i, node := range ns { 58 node := node 59 nodeUnmarshal := fakeUnmarshal 60 for _, n := range nodes.Nodes { 61 if n.IDVal == node.ID() { 62 nodeUnmarshal = n.Unmarshal 63 break 64 } 65 } 66 67 wrapped, err := nw(nodeUnmarshal, node) 68 if err != nil { 69 errs = append(errs, err) 70 } 71 72 if wrapped != nil { 73 wrappedNodes[i] = wrapped 74 } 75 } 76 77 var err error 78 if len(errs) > 0 { 79 err = MultiError{Errs: errs} 80 } 81 82 return nodeWrapperGraphRenderer{ 83 GraphRenderer: graph, 84 nodes: wrappedNodes, 85 }, err 86 } 87 } 88 89 // A Linter is used to lint the yaml file to find YAML-related errors, such 90 // as duplicated ids. 91 // 92 // A Linter has access to the unmarshal function so it can focus on the yaml. 93 // To check errors in the graph, use a Checker. 94 // 95 // To use lintes with goflow, the easiest way is to wrap them with FromLinter 96 // and place them first in your list of wrappers. 97 type Linter func(unmarshal func(interface{}) error) error 98 99 // FromLinter transforms a linter into a GraphWrapper 100 func FromLinter(lint Linter) GraphWrapper { 101 return func(unmarshal func(interface{}) error, graph GraphRenderer) (GraphRenderer, error) { 102 return nil, lint(unmarshal) 103 } 104 } 105 106 // A Checker is used to check that a graph follows the guide lines you set, such as 107 // checking that no node is never used. 108 // 109 // A Checker has access to the GraphRenderer, making it a good fit for coherence checks 110 // on the graph. As it does not have access to the unmarshal function, it cannot 111 // lint the yaml. To do that, use a Linter. 112 // 113 // To use checkers with goflow, the easiest way is to wrap them with FromChecker 114 // and place the last in you list of wrappers. 115 type Checker func(graph GraphRenderer) error 116 117 // FromChecker transforms a checker into a GraphWrapper 118 func FromChecker(check Checker) GraphWrapper { 119 return func(unmarshal func(interface{}) error, graph GraphRenderer) (GraphRenderer, error) { 120 if err := check(graph); err != nil { 121 return nil, err 122 } 123 return graph, nil 124 } 125 }