github.com/awesome-flow/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/cast/mapper.go (about)

     1  package cast
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/awesome-flow/flow/pkg/types"
     7  )
     8  
     9  // Mapper is a generic interface for mapping actors. These co-exist hand-by-hand
    10  // with Converters. Converters are trivial convert-or-give-up actors, designed
    11  // to act as a part of a more complex conversion/mapping logic. Mappers are not
    12  // supposed to do conversion but mapping: produce complex structures from the
    13  // input maps.
    14  type Mapper interface {
    15  	Map(kv *types.KeyValue) (*types.KeyValue, error)
    16  }
    17  
    18  // MapperNode is a data structure representing a trie node holding a Mapper and
    19  // trie structure children.
    20  type MapperNode struct {
    21  	Mpr      Mapper
    22  	Children map[string]*MapperNode
    23  }
    24  
    25  // NewMapperNode is the constructor for MapperNode.
    26  func NewMapperNode() *MapperNode {
    27  	return &MapperNode{}
    28  }
    29  
    30  // Insert effectively places the Mapper under the specified Key in the trie
    31  // structure. If the trie path does not exist, it creates the necessary nodes.
    32  // Insert supports wildcards in the Key path. This effectively relaxes the Find
    33  // operation strictness.
    34  //
    35  // Example: Insert(Key("foo.*.baz"), m) means: a Find(key) lookup should follow
    36  // path like: foo -> <any key> -> baz. Both foo.bar.baz and foo.moo.baz will
    37  // match the search and return m.
    38  // Wildcards have priority: a star match has a lower precedence than the exact
    39  // match.
    40  //
    41  // Example:
    42  //   Insert(Key("foo.*.baz"), m1)
    43  //   Insert(Key("boo.bar.baz"), m2)
    44  // In this case Find(Key("foo.moo.baz")) returns m2, whereas
    45  // Find(Key("foo.bar.baz")) returns m1 because it's an exact match.
    46  func (mn *MapperNode) Insert(key types.Key, mpr Mapper) *MapperNode {
    47  	var ptr *MapperNode
    48  	// Non-empty key check prevents users from accessing the root node
    49  	if len(key) > 0 {
    50  		ptr = mn
    51  		for _, k := range key {
    52  			if ptr.Children == nil {
    53  				ptr.Children = make(map[string]*MapperNode)
    54  			}
    55  			if _, ok := ptr.Children[k]; !ok {
    56  				ptr.Children[k] = NewMapperNode()
    57  			}
    58  			ptr = ptr.Children[k]
    59  		}
    60  		ptr.Mpr = mpr
    61  	}
    62  
    63  	return ptr
    64  }
    65  
    66  // Find performs a lookup of a relevant MapperNode in the trie structure by
    67  // following the provided Key path. If the needle node could not be found,
    68  // returns nil.
    69  // Find supports wildcards. See `Insert()` for more details.
    70  func (mn *MapperNode) Find(key types.Key) *MapperNode {
    71  	if len(key) == 0 {
    72  		return mn
    73  	}
    74  	for _, nextK := range []string{key[0], "*"} {
    75  		if next, ok := mn.Children[nextK]; ok {
    76  			if res := next.Find(key[1:]); res != nil {
    77  				return res
    78  			}
    79  		}
    80  	}
    81  	return nil
    82  }
    83  
    84  // DefineSchema is the primary way to bulk-register mappers in a MapperNode.
    85  // Schema is a very flexible structure. See Schema docs for more details.
    86  // If Schema is defined as a map[string]Schema, MapperNode will explicitly look
    87  // up for a key called __self__ which is the way to provide a Mapper for the
    88  // original node that also encorporates parent-children relationships.
    89  //
    90  // Example: a flat Schema might be defined like:
    91  // schema := map[string]Schema{"foo": FooMapper}
    92  //
    93  // It's also possible to provide a Converter instead of a Mapper, in this case
    94  // it would be automatically converted to a Mapper:
    95  // schema := map[string]Schema{"foo": FooConverter}
    96  //
    97  // If `foo` is a parent node to a set of other keys and there is a composite
    98  // mapper for `foo` itself, it might be achieved this way:
    99  // schema := map[string]Schema{"foo": map[string]Schema{"__self__": FooMapper, "bar": BarMapper, "moo": MooConverter}}
   100  // In this case a `foo` conversion lookup would be resolved this way:
   101  // 1. Convert `moo` using MooMapper, convert `bar` using BarMapper.
   102  // 2. Convert `foo` using FooMapper providing map[string]Value{"moo": MooVal, "bar": BarVal}
   103  // 3. Return result.
   104  //
   105  // __self__ might be set to nil in the schema definition in order to emphasise
   106  // an absence of the mapper for the parental key. It's fully equivalent to
   107  // no-definition for key __self__.
   108  func (mn *MapperNode) DefineSchema(s Schema) error {
   109  	return mn.doDefineSchema(types.NewKey(""), s)
   110  }
   111  
   112  func (mn *MapperNode) doDefineSchema(key types.Key, schema Schema) error {
   113  	if schema == nil {
   114  		return nil
   115  	} else if mpr, ok := schema.(Mapper); ok {
   116  		mn.Insert(key, mpr)
   117  	} else if cnv, ok := schema.(Converter); ok {
   118  		mn.Insert(key, NewConvMapper(cnv))
   119  	} else if smap, ok := schema.(map[string]Schema); ok {
   120  		if self, ok := smap["__self__"]; ok {
   121  			// self: nil is used to emphasize an empty mapper for a federation structure
   122  			if self != nil {
   123  				if err := mn.doDefineSchema(key, self); err != nil {
   124  					return err
   125  				}
   126  			}
   127  		}
   128  		for subKey, subSchema := range smap {
   129  			if subKey == "__self__" {
   130  				continue
   131  			}
   132  			if err := mn.doDefineSchema(append(key, subKey), subSchema); err != nil {
   133  				return err
   134  			}
   135  		}
   136  	} else {
   137  		return fmt.Errorf("Unexpected schema definition type for key %q: %#v",
   138  			key.String(), schema)
   139  	}
   140  	return nil
   141  }
   142  
   143  // Map performs the actual mapping of the key-value pair.
   144  func (mn *MapperNode) Map(kv *types.KeyValue) (*types.KeyValue, error) {
   145  	if ptr := mn.Find(kv.Key); ptr != nil && ptr.Mpr != nil {
   146  		if mkv, err := ptr.Mpr.Map(kv); err != nil {
   147  			return nil, err
   148  		} else {
   149  			return mkv, nil
   150  		}
   151  	}
   152  	return kv, nil
   153  }
   154  
   155  // ConvMapper is a helper wrapper that turns a single Converter into a Mapper
   156  // structure with the expected bahavior: if Converter fails to convert, the
   157  // wrapper Mapper returns an error.
   158  type ConvMapper struct {
   159  	conv Converter
   160  }
   161  
   162  var _ Mapper = (*ConvMapper)(nil)
   163  
   164  // NewConvMapper is the constructor for ConvMapper.
   165  func NewConvMapper(conv Converter) *ConvMapper {
   166  	return &ConvMapper{conv}
   167  }
   168  
   169  // Map returns a key-value pair if the Converter recognised the value.
   170  // Returns nil, err otherwise.
   171  func (cm *ConvMapper) Map(kv *types.KeyValue) (*types.KeyValue, error) {
   172  	if mkv, ok := cm.conv.Convert(kv); ok {
   173  		return mkv, nil
   174  	}
   175  	return nil, fmt.Errorf("Failed to convert value %#v for key %#v", kv.Key, kv.Value)
   176  }