github.com/netdata/go.d.plugin@v0.58.1/pkg/stm/stm.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package stm 4 5 import ( 6 "fmt" 7 "log" 8 "reflect" 9 "strconv" 10 "strings" 11 ) 12 13 const ( 14 fieldTagName = "stm" 15 structKey = "STMKey" 16 ) 17 18 type ( 19 Value interface { 20 WriteTo(rv map[string]int64, key string, mul, div int) 21 } 22 ) 23 24 // ToMap converts struct to a map[string]int64 based on 'stm' tags 25 func ToMap(s ...interface{}) map[string]int64 { 26 rv := map[string]int64{} 27 for _, v := range s { 28 value := reflect.Indirect(reflect.ValueOf(v)) 29 toMap(value, rv, "", 1, 1) 30 } 31 return rv 32 } 33 34 func toMap(value reflect.Value, rv map[string]int64, key string, mul, div int) { 35 if !value.IsValid() { 36 log.Panicf("value is not valid key=%s", key) 37 } 38 if value.CanInterface() { 39 val, ok := value.Interface().(Value) 40 if ok { 41 val.WriteTo(rv, key, mul, div) 42 return 43 } 44 } 45 switch value.Kind() { 46 case reflect.Ptr: 47 convertPtr(value, rv, key, mul, div) 48 case reflect.Struct: 49 convertStruct(value, rv, key) 50 case reflect.Array, reflect.Slice: 51 convertArraySlice(value, rv, key, mul, div) 52 case reflect.Map: 53 convertMap(value, rv, key, mul, div) 54 case reflect.Bool: 55 convertBool(value, rv, key) 56 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 57 convertInteger(value, rv, key, mul, div) 58 case reflect.Float32, reflect.Float64: 59 convertFloat(value, rv, key, mul, div) 60 case reflect.Interface: 61 convertInterface(value, rv, key, mul, div) 62 default: 63 log.Panicf("unsupported data type: %v", value.Kind()) 64 } 65 } 66 67 func convertPtr(value reflect.Value, rv map[string]int64, key string, mul, div int) { 68 if !value.IsNil() { 69 toMap(value.Elem(), rv, key, mul, div) 70 } 71 } 72 73 func convertStruct(value reflect.Value, rv map[string]int64, key string) { 74 t := value.Type() 75 k := value.FieldByName(structKey) 76 if k.Kind() == reflect.String { 77 key = joinPrefix(key, k.String()) 78 } 79 for i := 0; i < t.NumField(); i++ { 80 ft := t.Field(i) 81 tag, ok := ft.Tag.Lookup(fieldTagName) 82 if !ok || ft.Name == structKey { 83 continue 84 } 85 value := value.Field(i) 86 prefix, mul, div := parseTag(tag) 87 toMap(value, rv, joinPrefix(key, prefix), mul, div) 88 } 89 } 90 91 func convertMap(value reflect.Value, rv map[string]int64, key string, mul, div int) { 92 if value.IsNil() { 93 log.Panicf("value is nil key=%s", key) 94 } 95 for _, k := range value.MapKeys() { 96 toMap(value.MapIndex(k), rv, joinPrefix(key, k.String()), mul, div) 97 } 98 } 99 100 func convertArraySlice(value reflect.Value, rv map[string]int64, key string, mul, div int) { 101 for i := 0; i < value.Len(); i++ { 102 toMap(value.Index(i), rv, key, mul, div) 103 } 104 } 105 106 func convertBool(value reflect.Value, rv map[string]int64, key string) { 107 if _, ok := rv[key]; ok { 108 log.Panic("duplicate key: ", key) 109 } 110 if value.Bool() { 111 rv[key] = 1 112 } else { 113 rv[key] = 0 114 } 115 } 116 117 func convertInteger(value reflect.Value, rv map[string]int64, key string, mul, div int) { 118 if _, ok := rv[key]; ok { 119 log.Panic("duplicate key: ", key) 120 } 121 intVal := value.Int() 122 rv[key] = intVal * int64(mul) / int64(div) 123 } 124 125 func convertFloat(value reflect.Value, rv map[string]int64, key string, mul, div int) { 126 if _, ok := rv[key]; ok { 127 log.Panic("duplicate key: ", key) 128 } 129 floatVal := value.Float() 130 rv[key] = int64(floatVal * float64(mul) / float64(div)) 131 } 132 133 func convertInterface(value reflect.Value, rv map[string]int64, key string, mul, div int) { 134 fv := reflect.ValueOf(value.Interface()) 135 toMap(fv, rv, key, mul, div) 136 } 137 138 func joinPrefix(prefix, key string) string { 139 if prefix == "" { 140 return key 141 } 142 if key == "" { 143 return prefix 144 } 145 return prefix + "_" + key 146 } 147 148 func parseTag(tag string) (prefix string, mul int, div int) { 149 tokens := strings.Split(tag, ",") 150 mul = 1 151 div = 1 152 var err error 153 switch len(tokens) { 154 case 3: 155 div, err = strconv.Atoi(tokens[2]) 156 if err != nil { 157 log.Panic(err) 158 } 159 fallthrough 160 case 2: 161 mul, err = strconv.Atoi(tokens[1]) 162 if err != nil { 163 log.Panic(err) 164 } 165 fallthrough 166 case 1: 167 prefix = tokens[0] 168 default: 169 log.Panic(fmt.Errorf("invalid tag format: %s", tag)) 170 } 171 return 172 }