vibe-proxy/backend/internal/credentialweight/weight.go
2026-08-24 00:10:41 +02:00

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")
}
}