github.com/Jeffail/benthos/v3@v3.65.0/lib/message/util.go (about) 1 package message 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/Jeffail/benthos/v3/lib/message/metadata" 8 "github.com/Jeffail/benthos/v3/lib/types" 9 ) 10 11 //------------------------------------------------------------------------------ 12 13 // SetAllMetadata sets the metadata of all message parts to match a provided 14 // metadata implementation. 15 func SetAllMetadata(m types.Message, meta types.Metadata) { 16 lazy := metadata.LazyCopy(meta) 17 m.Iter(func(i int, p types.Part) error { 18 p.SetMetadata(lazy) 19 return nil 20 }) 21 } 22 23 // GetAllBytes returns a 2D byte slice representing the raw byte content of the 24 // parts of a message. 25 func GetAllBytes(m types.Message) [][]byte { 26 if m.Len() == 0 { 27 return nil 28 } 29 parts := make([][]byte, m.Len()) 30 m.Iter(func(i int, p types.Part) error { 31 parts[i] = p.Get() 32 return nil 33 }) 34 return parts 35 } 36 37 // GetAllBytesLen returns total length of message content in bytes 38 func GetAllBytesLen(m types.Message) int { 39 if m.Len() == 0 { 40 return 0 41 } 42 length := 0 43 m.Iter(func(i int, p types.Part) error { 44 length += len(p.Get()) 45 return nil 46 }) 47 return length 48 } 49 50 //------------------------------------------------------------------------------ 51 52 // MetaPartCopy creates a new empty message part by copying any meta fields 53 // (metadata, context, etc) from a reference part. 54 func MetaPartCopy(p types.Part) types.Part { 55 newPart := WithContext(GetContext(p), NewPart(nil)) 56 newPart.SetMetadata(p.Metadata().Copy()) 57 return newPart 58 } 59 60 //------------------------------------------------------------------------------ 61 62 func cloneMap(oldMap map[string]interface{}) (map[string]interface{}, error) { 63 var err error 64 newMap := make(map[string]interface{}, len(oldMap)) 65 for k, v := range oldMap { 66 if newMap[k], err = cloneGeneric(v); err != nil { 67 return nil, err 68 } 69 } 70 return newMap, nil 71 } 72 73 func cloneCheekyMap(oldMap map[interface{}]interface{}) (map[interface{}]interface{}, error) { 74 var err error 75 newMap := make(map[interface{}]interface{}, len(oldMap)) 76 for k, v := range oldMap { 77 if newMap[k], err = cloneGeneric(v); err != nil { 78 return nil, err 79 } 80 } 81 return newMap, nil 82 } 83 84 func cloneSlice(oldSlice []interface{}) ([]interface{}, error) { 85 var err error 86 newSlice := make([]interface{}, len(oldSlice)) 87 for i, v := range oldSlice { 88 if newSlice[i], err = cloneGeneric(v); err != nil { 89 return nil, err 90 } 91 } 92 return newSlice, nil 93 } 94 95 // cloneGeneric is a utility function that recursively copies a generic 96 // structure usually resulting from a JSON parse. 97 func cloneGeneric(root interface{}) (interface{}, error) { 98 switch t := root.(type) { 99 case map[string]interface{}: 100 return cloneMap(t) 101 case map[interface{}]interface{}: 102 return cloneCheekyMap(t) 103 case []interface{}: 104 return cloneSlice(t) 105 case string, []byte, json.Number, uint64, int, int64, float64, bool, json.RawMessage: 106 return t, nil 107 default: 108 // Oops, this means we have 'dirty' types within the JSON object. Our 109 // only way to fallback is to marshal/unmarshal the structure, gross! 110 if b, err := json.Marshal(root); err == nil { 111 var rootCopy interface{} 112 if err = json.Unmarshal(b, &rootCopy); err == nil { 113 return rootCopy, nil 114 } 115 } 116 return nil, fmt.Errorf("unrecognised type: %T", t) 117 } 118 } 119 120 // CopyJSON recursively creates a deep copy of a JSON structure extracted from a 121 // message part. 122 func CopyJSON(root interface{}) (interface{}, error) { 123 return cloneGeneric(root) 124 } 125 126 //------------------------------------------------------------------------------