package perfdata // import nocternity.net/gomonop/pkg/perfdata import ( "fmt" "strings" ) // Performance data range. type Range struct { start string end string inside bool } // Creates a performance data range from -inf to 0 and from the specified // value to +inf. func RangeMax(max string) *Range { if !valueCheck.MatchString(max) { panic("invalid performance data range maximum value") } pdRange := &Range{} pdRange.start = "0" pdRange.end = max return pdRange } // Creates a performance data range from -inf to the specified minimal value // and from the specified maximal value to +inf. func RangeMinMax(min, max string) *Range { if !valueCheck.MatchString(max) { panic("invalid performance data range maximum value") } if !rangeMinCheck.MatchString(min) { panic("invalid performance data range minimum value") } pdRange := &Range{} pdRange.start = min pdRange.end = max return pdRange } // Inverts the range. func (r *Range) Inside() *Range { r.inside = true return r } // Generates the range's string representation so it can be sent to the // monitoring system. func (r *Range) String() string { var start, inside string switch r.start { case "": start = "~" case "0": start = "" default: start = r.start } if r.inside { inside = "@" } return inside + start + ":" + r.end } // A state of the range parser. type rangeParserState int const ( rpsInit = iota // Initial state rpsExpectStart // Expect the start of the range rpsInStart // Reading the start of the range rpsExpectColon // Expect the colon that separates the start and end rpsExpectEnd // Expect the end of the range rpsInEnd // Reading the end of the range ) // An error emitted by the range parser. type rangeParserError struct { input string position int message string } // parseError creates a new range parser error. func parseError(input string, position int, message string) *rangeParserError { return &rangeParserError{ input: input, position: position, message: message, } } // Error implements the error interface for the range parser error type. func (rpe *rangeParserError) Error() string { return fmt.Sprintf("in `%s' at position %d: %s", rpe.input, rpe.position, rpe.message) } // Try to parse a string into a performance data range. func ParseRange(input string) (*Range, error) { runes := []rune(input) index := 0 state := rpsInit parsed := Range{} startOfStart := 0 startOfEnd := 0 strBuilder := strings.Builder{} for index < len(runes) { curRune := runes[index] switch state { case rpsInit: if curRune == '@' { parsed.inside = true index += 1 } state = rpsExpectStart case rpsExpectStart: switch curRune { case ':': parsed.start = "0" state = rpsExpectEnd case '~': state = rpsExpectColon default: strBuilder.WriteRune(curRune) startOfStart = index state = rpsInStart } index += 1 case rpsInStart: switch curRune { case ':': parsed.start = strBuilder.String() if !valueCheck.MatchString(parsed.start) { return nil, parseError(input, startOfStart, "invalid start value") } strBuilder.Reset() state = rpsExpectEnd default: strBuilder.WriteRune(curRune) } index += 1 case rpsExpectColon: switch curRune { case ':': state = rpsExpectEnd index += 1 default: return nil, parseError(input, index, "expected ':'") } case rpsExpectEnd: startOfEnd = index state = rpsInEnd case rpsInEnd: strBuilder.WriteRune(curRune) index += 1 } } if state != rpsInEnd { return nil, parseError(input, index, "unexpected end of input") } parsed.end = strBuilder.String() if !valueCheck.MatchString(parsed.end) { return nil, parseError(input, startOfEnd, "invalid end value") } return &parsed, nil }