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  }