<commit_before>package config

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/Knetic/govaluate"
)

// MetricMultiMap Value
type MetricMultiMap struct {
	IType string
	IName string
	IConv string
	Value interface{}
}

//SnmpMetricCfg Metric config
type SnmpMetricCfg struct {
	ID          string         `xorm:"'id' unique" binding:"Required"` //name of the key in the config array
	FieldName   string         `xorm:"field_name" binding:"Required"`
	Description string         `xorm:"description"`
	BaseOID     string         `xorm:"baseoid"`
	DataSrcType string         `xorm:"datasrctype"`
	GetRate     bool           `xorm:"getrate"` //ony Valid with COUNTERS
	Scale       float64        `xorm:"scale"`
	Shift       float64        `xorm:"shift"`
	IsTag       bool           `xorm:"'istag' default 0"` //Not Valid on  MULTISTRINGPARSER
	ExtraData   string         `xorm:"extradata"`         //Only Valid with STRINGPARSER, MULTISTRINGPARSER, STRINGEVAL , BITS , BITSCHK, ENUM
	Names       map[int]string `xorm:"-" json:"-"`        //BitString Name array
}

/*
Init Initializes Metric
3.- Check minimal data is set  (pending)
name, BaseOID BaseOID beginning with "."
fieldname != null
*/
// Init initialize metrics
func (m *SnmpMetricCfg) Init() error {
	//valIDate config values
	if len(m.FieldName) == 0 {
		return errors.New("FieldName not set in metric Config " + m.ID)
	}
	if len(m.BaseOID) == 0 && m.DataSrcType != "STRINGEVAL" && m.DataSrcType != "CONDITIONEVAL" {
		return fmt.Errorf("BaseOid not set in metric Config %s type  %s"+m.ID, m.DataSrcType)
	}
	//https://tools.ietf.org/html/rfc2578 (SMIv2)
	//https://tools.ietf.org/html/rfc2579 (Textual Conventions for SMIv2)
	//https://tools.ietf.org/html/rfc2851 (Textual Conventions for Internet Network Address)
	switch m.DataSrcType {
	case "INTEGER", "Integer32":
	case "Gauge32":
	case "UInteger32", "Unsigned32":
	case "Counter32", "COUNTER32": //raw and cooked increment of Counter32
	case "Counter64", "COUNTER64": //raw and Cooked increment of Counter64
	case "COUNTERXX": //raw and Coocked increment with non_negative behaviour of Counters
	case "TimeTicks", "TIMETICKS": //raw and cooked to second of timeticks
	case "BITS", "BITSCHK":
	case "ENUM":
	case "OCTETSTRING":
	case "OID":
	case "HWADDR":
	case "IpAddress":
	case "STRINGPARSER":
	case "MULTISTRINGPARSER":
	case "STRINGEVAL":
	case "CONDITIONEVAL":
	default:
		return errors.New("UnkNown DataSourceType:" + m.DataSrcType + " in metric Config " + m.ID)
	}
	if m.DataSrcType == "BITSCHK" {
		if len(m.ExtraData) == 0 {
			return errors.New("BITSCHK type requires extradata to work " + m.ID)
		}
		_, err := strconv.Atoi(m.ExtraData)
		if err != nil {
			return errors.New("BITSCHK type requires extradata to be a positive integer to work: ERROR " + err.Error())
		}
	}
	if m.DataSrcType == "BITS" {
		if len(m.ExtraData) == 0 {
			return errors.New("BITS type requires extradata to work " + m.ID)
		}
		//named bits array construction for this Config
		re := regexp.MustCompile("([a-zA-Z0-9]+)\\(([0-9]+)\\)")
		m.Names = make(map[int]string)
		str := re.FindAllStringSubmatch(m.ExtraData, -1)
		for _, x := range str {
			i, _ := strconv.Atoi(x[2])
			m.Names[i] = x[1]
		}
	}
	if m.DataSrcType == "ENUM" {
		if len(m.ExtraData) == 0 {
			return errors.New("ENUM type requires extradata to work " + m.ID)
		}
		//named enum array construction for this Config
		re := regexp.MustCompile("([a-zA-Z0-9\\-]+)\\s*\\(\\s*([0-9]+)\\s*\\)")
		m.Names = make(map[int]string)
		str := re.FindAllStringSubmatch(m.ExtraData, -1)
		for _, x := range str {
			i, _ := strconv.Atoi(x[2])
			m.Names[i] = fmt.Sprintf("%s(%d)", x[1], i)
		}
	}
	if m.DataSrcType != "STRINGEVAL" && m.DataSrcType != "CONDITIONEVAL" && !strings.HasPrefix(m.BaseOID, ".") {
		return errors.New("Bad BaseOid format:" + m.BaseOID + " in metric Config " + m.ID)
	}
	if m.DataSrcType == "STRINGPARSER" && len(m.ExtraData) == 0 {
		return errors.New("STRINGPARSER type requires extradata to work " + m.ID)
	}

	if m.DataSrcType == "MULTISTRINGPARSER" {
		if len(m.ExtraData) == 0 {
			return errors.New("MULTISTRINGPARSER type requires extradata to work " + m.ID)
		}
		//Check Field Syntax.<commit_msg>Add support for whitespace, dash and underscore in BITS and ENUM arrays<commit_after>package config

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/Knetic/govaluate"
)

// MetricMultiMap Value
type MetricMultiMap struct {
	IType string
	IName string
	IConv string
	Value interface{}
}

//SnmpMetricCfg Metric config
type SnmpMetricCfg struct {
	ID          string         `xorm:"'id' unique" binding:"Required"` //name of the key in the config array
	FieldName   string         `xorm:"field_name" binding:"Required"`
	Description string         `xorm:"description"`
	BaseOID     string         `xorm:"baseoid"`
	DataSrcType string         `xorm:"datasrctype"`
	GetRate     bool           `xorm:"getrate"` //ony Valid with COUNTERS
	Scale       float64        `xorm:"scale"`
	Shift       float64        `xorm:"shift"`
	IsTag       bool           `xorm:"'istag' default 0"` //Not Valid on  MULTISTRINGPARSER
	ExtraData   string         `xorm:"extradata"`         //Only Valid with STRINGPARSER, MULTISTRINGPARSER, STRINGEVAL , BITS , BITSCHK, ENUM
	Names       map[int]string `xorm:"-" json:"-"`        //BitString Name array
}

/*
Init Initializes Metric
3.- Check minimal data is set  (pending)
name, BaseOID BaseOID beginning with "."
fieldname != null
*/
// Init initialize metrics
func (m *SnmpMetricCfg) Init() error {
	//valIDate config values
	if len(m.FieldName) == 0 {
		return errors.New("FieldName not set in metric Config " + m.ID)
	}
	if len(m.BaseOID) == 0 && m.DataSrcType != "STRINGEVAL" && m.DataSrcType != "CONDITIONEVAL" {
		return fmt.Errorf("BaseOid not set in metric Config %s type  %s"+m.ID, m.DataSrcType)
	}
	//https://tools.ietf.org/html/rfc2578 (SMIv2)
	//https://tools.ietf.org/html/rfc2579 (Textual Conventions for SMIv2)
	//https://tools.ietf.org/html/rfc2851 (Textual Conventions for Internet Network Address)
	switch m.DataSrcType {
	case "INTEGER", "Integer32":
	case "Gauge32":
	case "UInteger32", "Unsigned32":
	case "Counter32", "COUNTER32": //raw and cooked increment of Counter32
	case "Counter64", "COUNTER64": //raw and Cooked increment of Counter64
	case "COUNTERXX": //raw and Coocked increment with non_negative behaviour of Counters
	case "TimeTicks", "TIMETICKS": //raw and cooked to second of timeticks
	case "BITS", "BITSCHK":
	case "ENUM":
	case "OCTETSTRING":
	case "OID":
	case "HWADDR":
	case "IpAddress":
	case "STRINGPARSER":
	case "MULTISTRINGPARSER":
	case "STRINGEVAL":
	case "CONDITIONEVAL":
	default:
		return errors.New("UnkNown DataSourceType:" + m.DataSrcType + " in metric Config " + m.ID)
	}
	if m.DataSrcType == "BITSCHK" {
		if len(m.ExtraData) == 0 {
			return errors.New("BITSCHK type requires extradata to work " + m.ID)
		}
		_, err := strconv.Atoi(m.ExtraData)
		if err != nil {
			return errors.New("BITSCHK type requires extradata to be a positive integer to work: ERROR " + err.Error())
		}
	}
	if m.DataSrcType == "BITS" {
		if len(m.ExtraData) == 0 {
			return errors.New("BITS type requires extradata to work " + m.ID)
		}
		//named bits array construction for this Config
		re := regexp.MustCompile("([a-zA-Z0-9\\-_]+)\\s*\\(\\s*([0-9]+)\\s*\\)")
		m.Names = make(map[int]string)
		str := re.FindAllStringSubmatch(m.ExtraData, -1)
		for _, x := range str {
			i, _ := strconv.Atoi(x[2])
			m.Names[i] = x[1]
		}
	}
	if m.DataSrcType == "ENUM" {
		if len(m.ExtraData) == 0 {
			return errors.New("ENUM type requires extradata to work " + m.ID)
		}
		//named enum array construction for this Config
		re := regexp.MustCompile("([a-zA-Z0-9\\-_]+)\\s*\\(\\s*([0-9]+)\\s*\\)")
		m.Names = make(map[int]string)
		str := re.FindAllStringSubmatch(m.ExtraData, -1)
		for _, x := range str {
			i, _ := strconv.Atoi(x[2])
			m.Names[i] = fmt.Sprintf("%s(%d)", x[1], i)
		}
	}
	if m.DataSrcType != "STRINGEVAL" && m.DataSrcType != "CONDITIONEVAL" && !strings.HasPrefix(m.BaseOID, ".") {
		return errors.New("Bad BaseOid format:" + m.BaseOID + " in metric Config " + m.ID)
	}
	if m.DataSrcType == "STRINGPARSER" && len(m.ExtraData) == 0 {
		return errors.New("STRINGPARSER type requires extradata to work " + m.ID)
	}

	if m.DataSrcType == "MULTISTRINGPARSER" {
		if len(m.ExtraData) == 0 {
			return errors.New("MULTISTRINGPARSER type requires extradata to work " + m.ID)
		}
		//Check Field Syntax.
{'fraction_non_alphanumeric': 0.14900623309420205, 'fraction_numerical': 0.014347877219804775, 'mean_word_length': 3.4039357845675817, 'pattern_counts': {'":': 34, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6245618', 'n_tokens_mistral': 3239, 'n_tokens_neox': 3086, 'n_words': 804}