bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/opentsdb/name.go (about)

     1  package opentsdb
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"unicode"
     7  
     8  	"bosun.org/name"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  type openTsdbNameConfig struct {
    13  	invalidRuneReplacement string
    14  	basicValidator         name.RuneLevelValidator
    15  }
    16  
    17  // NewOpenTsdbNameProcessor constructs a new name.RuneLevelProcessor which can work with the OpenTSDB name format
    18  func NewOpenTsdbNameProcessor(invalidRuneReplacement string) (name.RuneLevelProcessor, error) {
    19  	bv, err := name.NewBasicValidator(false, func(r rune) bool {
    20  		return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '_' || r == '.' || r == '/'
    21  	})
    22  
    23  	if err != nil {
    24  		return nil, errors.Wrap(err, "Failed to construct basic validator")
    25  	}
    26  
    27  	result := &openTsdbNameConfig{invalidRuneReplacement: invalidRuneReplacement, basicValidator: bv}
    28  
    29  	return result, nil
    30  }
    31  
    32  // IsRuneValid returns true if an isolated rune is valid within an OpenTSDB format name
    33  func (c *openTsdbNameConfig) IsRuneValid(r rune) bool {
    34  	return c.basicValidator.IsRuneValid(r)
    35  }
    36  
    37  // IsValid returns true if a name is valid according the OpenTSDB rules
    38  func (c *openTsdbNameConfig) IsValid(name string) bool {
    39  	return c.basicValidator.IsValid(name)
    40  }
    41  
    42  // FormatName takes a name and attempts to ensure that is valid according to OpenTSDB rules.  If a names contains
    43  // any invalid runes then these are replaced with 'invalidRuneReplacement'.
    44  func (c *openTsdbNameConfig) FormatName(name string) (string, error) {
    45  	sb := strings.Builder{}
    46  	var err error
    47  	var lastRune rune
    48  	for _, r := range name {
    49  		if c.IsRuneValid(r) {
    50  			_, err = sb.WriteRune(r)
    51  		} else if lastRune != r { // don't do a 1 for 1 replacement on a run of invalid runes
    52  			_, err = sb.WriteString(c.invalidRuneReplacement)
    53  		}
    54  
    55  		if err != nil {
    56  			return "", errors.Wrap(err, fmt.Sprintf("Failed to format '%s'", name))
    57  		}
    58  
    59  		lastRune = r
    60  	}
    61  
    62  	result := sb.String()
    63  	if result == "" {
    64  		return "", errors.New("Name left empty after formatting")
    65  	}
    66  
    67  	return result, nil
    68  }