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  }