Initial commit

This commit is contained in:
2026-09-12 23:26:07 +02:00
commit a180fe4b52
35 changed files with 4921 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
package config
import (
"fmt"
"strconv"
"strings"
"time"
)
// Unlimited is the zero value used by both size and duration limits to mean
// "no limit". Callers must therefore always compare against 0 before enforcing.
const Unlimited = 0
var sizeUnits = []struct {
suffix string
mult int64
}{
{"KIB", 1 << 10}, {"MIB", 1 << 20}, {"GIB", 1 << 30}, {"TIB", 1 << 40},
{"KB", 1e3}, {"MB", 1e6}, {"GB", 1e9}, {"TB", 1e12},
{"K", 1 << 10}, {"M", 1 << 20}, {"G", 1 << 30}, {"T", 1 << 40},
{"B", 1},
}
// ParseSize accepts "2GiB", "500MB", "1048576", "unlimited" or "0". Binary
// units (KiB) and bare "K"/"M"/"G" are powers of 1024; "KB"/"MB"/"GB" are
// powers of 1000, per SI.
func ParseSize(s string) (int64, error) {
t := strings.ToUpper(strings.TrimSpace(s))
if t == "" || t == "0" || t == "UNLIMITED" || t == "NONE" {
return Unlimited, nil
}
mult := int64(1)
for _, u := range sizeUnits {
if strings.HasSuffix(t, u.suffix) {
t = strings.TrimSpace(strings.TrimSuffix(t, u.suffix))
mult = u.mult
break
}
}
n, err := strconv.ParseFloat(t, 64)
if err != nil {
return 0, fmt.Errorf("invalid size %q", s)
}
if n < 0 {
return 0, fmt.Errorf("invalid size %q: negative", s)
}
v := int64(n * float64(mult))
if v < 0 {
return 0, fmt.Errorf("invalid size %q: overflow", s)
}
return v, nil
}
// FormatSize renders a *limit* for humans, where zero means "no limit". Use
// FormatBytes for an actual quantity, where zero means zero.
func FormatSize(n int64) string {
if n == Unlimited {
return "unlimited"
}
return FormatBytes(n)
}
// FormatBytes renders a byte count for humans.
func FormatBytes(n int64) string {
units := []string{"B", "KiB", "MiB", "GiB", "TiB"}
f, i := float64(n), 0
for f >= 1024 && i < len(units)-1 {
f /= 1024
i++
}
if i == 0 {
return fmt.Sprintf("%d B", n)
}
return strings.TrimSuffix(strconv.FormatFloat(f, 'f', 2, 64), ".00") + " " + units[i]
}
// ParseDuration extends time.ParseDuration with "d" (day) and "w" (week)
// suffixes, and maps "never"/"0" to Unlimited. Durations are the one place the
// stdlib's vocabulary is genuinely too small: "72h" is a poor way to say "3d".
func ParseDuration(s string) (time.Duration, error) {
t := strings.ToLower(strings.TrimSpace(s))
if t == "" || t == "0" || t == "never" || t == "unlimited" || t == "none" {
return Unlimited, nil
}
// Expand a trailing d/w into hours so time.ParseDuration can finish the job.
for suffix, hours := range map[string]float64{"d": 24, "w": 168} {
if num, ok := strings.CutSuffix(t, suffix); ok {
n, err := strconv.ParseFloat(num, 64)
if err != nil {
return 0, fmt.Errorf("invalid duration %q", s)
}
t = strconv.FormatFloat(n*hours, 'f', -1, 64) + "h"
break
}
}
d, err := time.ParseDuration(t)
if err != nil {
return 0, fmt.Errorf("invalid duration %q", s)
}
if d < 0 {
return 0, fmt.Errorf("invalid duration %q: negative", s)
}
return d, nil
}
// FormatDuration is the human-facing inverse of ParseDuration.
func FormatDuration(d time.Duration) string {
switch {
case d == Unlimited:
return "never"
case d%(24*time.Hour) == 0:
return fmt.Sprintf("%dd", d/(24*time.Hour))
case d%time.Hour == 0:
return fmt.Sprintf("%dh", d/time.Hour)
default:
return d.String()
}
}