|
| 1 | +package lx |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "strings" |
| 6 | +) |
| 7 | + |
| 8 | +// Pair represents a key-value pair where the key is a string and the value is of any type. |
| 9 | +type Pair struct { |
| 10 | + Key string |
| 11 | + Value interface{} |
| 12 | +} |
| 13 | + |
| 14 | +// Fields represents a slice of key-value pairs. |
| 15 | +type Fields []Pair |
| 16 | + |
| 17 | +// Map converts the Fields slice to a map[string]interface{}. |
| 18 | +// This is useful for backward compatibility or when map operations are needed. |
| 19 | +// Example: |
| 20 | +// |
| 21 | +// fields := lx.Fields{{"user", "alice"}, {"age", 30}} |
| 22 | +// m := fields.Map() // Returns map[string]interface{}{"user": "alice", "age": 30} |
| 23 | +func (f Fields) Map() map[string]interface{} { |
| 24 | + m := make(map[string]interface{}, len(f)) |
| 25 | + for _, pair := range f { |
| 26 | + m[pair.Key] = pair.Value |
| 27 | + } |
| 28 | + return m |
| 29 | +} |
| 30 | + |
| 31 | +// Get returns the value for a given key and a boolean indicating if the key was found. |
| 32 | +// This provides O(n) lookup, which is fine for small numbers of fields. |
| 33 | +// Example: |
| 34 | +// |
| 35 | +// fields := lx.Fields{{"user", "alice"}, {"age", 30}} |
| 36 | +// value, found := fields.Get("user") // Returns "alice", true |
| 37 | +func (f Fields) Get(key string) (interface{}, bool) { |
| 38 | + for _, pair := range f { |
| 39 | + if pair.Key == key { |
| 40 | + return pair.Value, true |
| 41 | + } |
| 42 | + } |
| 43 | + return nil, false |
| 44 | +} |
| 45 | + |
| 46 | +// Filter returns a new Fields slice containing only pairs where the predicate returns true. |
| 47 | +// Example: |
| 48 | +// |
| 49 | +// fields := lx.Fields{{"user", "alice"}, {"password", "secret"}, {"age", 30}} |
| 50 | +// filtered := fields.Filter(func(key string, value interface{}) bool { |
| 51 | +// return key != "password" // Remove sensitive fields |
| 52 | +// }) |
| 53 | +func (f Fields) Filter(predicate func(key string, value interface{}) bool) Fields { |
| 54 | + result := make(Fields, 0, len(f)) |
| 55 | + for _, pair := range f { |
| 56 | + if predicate(pair.Key, pair.Value) { |
| 57 | + result = append(result, pair) |
| 58 | + } |
| 59 | + } |
| 60 | + return result |
| 61 | +} |
| 62 | + |
| 63 | +// Translate returns a new Fields slice with keys translated according to the provided mapping. |
| 64 | +// Keys not in the mapping are passed through unchanged. This is useful for adapters like Victoria. |
| 65 | +// Example: |
| 66 | +// |
| 67 | +// fields := lx.Fields{{"user", "alice"}, {"timestamp", time.Now()}} |
| 68 | +// translated := fields.Translate(map[string]string{ |
| 69 | +// "user": "username", |
| 70 | +// "timestamp": "ts", |
| 71 | +// }) |
| 72 | +// // Returns: {{"username", "alice"}, {"ts", time.Now()}} |
| 73 | +func (f Fields) Translate(mapping map[string]string) Fields { |
| 74 | + result := make(Fields, len(f)) |
| 75 | + for i, pair := range f { |
| 76 | + if newKey, ok := mapping[pair.Key]; ok { |
| 77 | + result[i] = Pair{Key: newKey, Value: pair.Value} |
| 78 | + } else { |
| 79 | + result[i] = pair |
| 80 | + } |
| 81 | + } |
| 82 | + return result |
| 83 | +} |
| 84 | + |
| 85 | +// Merge merges another Fields slice into this one, with the other slice's fields taking precedence |
| 86 | +// for duplicate keys (overwrites existing keys). |
| 87 | +// Example: |
| 88 | +// |
| 89 | +// base := lx.Fields{{"user", "alice"}, {"age", 30}} |
| 90 | +// additional := lx.Fields{{"age", 31}, {"city", "NYC"}} |
| 91 | +// merged := base.Merge(additional) |
| 92 | +// // Returns: {{"user", "alice"}, {"age", 31}, {"city", "NYC"}} |
| 93 | +func (f Fields) Merge(other Fields) Fields { |
| 94 | + result := make(Fields, 0, len(f)+len(other)) |
| 95 | + |
| 96 | + // Create a map to track which keys from 'other' we've seen |
| 97 | + seen := make(map[string]bool, len(other)) |
| 98 | + |
| 99 | + // First add all fields from 'f' |
| 100 | + result = append(result, f...) |
| 101 | + |
| 102 | + // Then add fields from 'other', overwriting duplicates |
| 103 | + for _, pair := range other { |
| 104 | + // Check if this key already exists in result |
| 105 | + found := false |
| 106 | + for i, existing := range result { |
| 107 | + if existing.Key == pair.Key { |
| 108 | + result[i] = pair // Overwrite |
| 109 | + found = true |
| 110 | + break |
| 111 | + } |
| 112 | + } |
| 113 | + if !found { |
| 114 | + result = append(result, pair) |
| 115 | + } |
| 116 | + seen[pair.Key] = true |
| 117 | + } |
| 118 | + |
| 119 | + return result |
| 120 | +} |
| 121 | + |
| 122 | +// String returns a human-readable string representation of the fields. |
| 123 | +// Example: |
| 124 | +// |
| 125 | +// fields := lx.Fields{{"user", "alice"}, {"age", 30}} |
| 126 | +// str := fields.String() // Returns: "[user=alice age=30]" |
| 127 | +func (f Fields) String() string { |
| 128 | + var builder strings.Builder |
| 129 | + builder.WriteString(LeftBracket) |
| 130 | + for i, pair := range f { |
| 131 | + if i > 0 { |
| 132 | + builder.WriteString(Space) |
| 133 | + } |
| 134 | + builder.WriteString(pair.Key) |
| 135 | + builder.WriteString("=") |
| 136 | + builder.WriteString(fmt.Sprint(pair.Value)) |
| 137 | + } |
| 138 | + builder.WriteString(RightBracket) |
| 139 | + return builder.String() |
| 140 | +} |
0 commit comments