100 lines
2.5 KiB
Go
100 lines
2.5 KiB
Go
// Package credentialweight defines shared credential weight validation and parsing.
|
|
package credentialweight
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// Default is used when a credential does not define a weight.
|
|
Default int64 = 1
|
|
// Max bounds scheduler arithmetic while allowing practical proportional routing.
|
|
Max int64 = 1_000_000
|
|
)
|
|
|
|
// Normalize validates and normalizes an explicit weight. Non-positive values are
|
|
// valid and normalize to zero, which excludes the credential from weighted routing.
|
|
func Normalize(weight int64) (int64, error) {
|
|
if weight <= 0 {
|
|
return 0, nil
|
|
}
|
|
if weight > Max {
|
|
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
|
}
|
|
return weight, nil
|
|
}
|
|
|
|
// ParseString parses a scheduler attribute. An empty value uses the default weight.
|
|
func ParseString(raw string) (int64, error) {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return Default, nil
|
|
}
|
|
weight, errParse := strconv.ParseInt(raw, 10, 64)
|
|
if errParse != nil {
|
|
return 0, fmt.Errorf("weight must be an integer: %w", errParse)
|
|
}
|
|
return Normalize(weight)
|
|
}
|
|
|
|
// ParseValue parses a JSON-compatible auth-file metadata value.
|
|
func ParseValue(value any) (int64, error) {
|
|
switch typed := value.(type) {
|
|
case int:
|
|
return Normalize(int64(typed))
|
|
case int8:
|
|
return Normalize(int64(typed))
|
|
case int16:
|
|
return Normalize(int64(typed))
|
|
case int32:
|
|
return Normalize(int64(typed))
|
|
case int64:
|
|
return Normalize(typed)
|
|
case uint:
|
|
if uint64(typed) > uint64(Max) {
|
|
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
|
}
|
|
return int64(typed), nil
|
|
case uint8:
|
|
return int64(typed), nil
|
|
case uint16:
|
|
return int64(typed), nil
|
|
case uint32:
|
|
if uint64(typed) > uint64(Max) {
|
|
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
|
}
|
|
return int64(typed), nil
|
|
case uint64:
|
|
if typed > uint64(Max) {
|
|
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
|
}
|
|
return int64(typed), nil
|
|
case float64:
|
|
if math.IsNaN(typed) || math.IsInf(typed, 0) || math.Trunc(typed) != typed {
|
|
return 0, fmt.Errorf("weight must be an integer")
|
|
}
|
|
if typed <= 0 {
|
|
return 0, nil
|
|
}
|
|
if typed > float64(Max) {
|
|
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
|
}
|
|
return int64(typed), nil
|
|
case float32:
|
|
return ParseValue(float64(typed))
|
|
case json.Number:
|
|
weight, errParse := typed.Int64()
|
|
if errParse != nil {
|
|
return 0, fmt.Errorf("weight must be an integer: %w", errParse)
|
|
}
|
|
return Normalize(weight)
|
|
case string:
|
|
return ParseString(typed)
|
|
default:
|
|
return 0, fmt.Errorf("weight must be an integer")
|
|
}
|
|
}
|