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 }