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 }