118 lines
2.7 KiB
Go
118 lines
2.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type ByteSize int64
|
|
|
|
type Duration time.Duration
|
|
|
|
func (size *ByteSize) UnmarshalYAML(value *yaml.Node) error {
|
|
if value.Kind != yaml.ScalarNode || value.Tag != "!!str" {
|
|
return fmt.Errorf("size must be a string with B, KB, MB, or GB suffix")
|
|
}
|
|
|
|
var raw string
|
|
if err := value.Decode(&raw); err != nil {
|
|
return err
|
|
}
|
|
parsed, err := ParseByteSize(raw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*size = parsed
|
|
return nil
|
|
}
|
|
|
|
func (size ByteSize) String() string {
|
|
value := int64(size)
|
|
if value == 0 {
|
|
return "0B"
|
|
}
|
|
units := []struct {
|
|
suffix string
|
|
multiplier int64
|
|
}{
|
|
{suffix: "GB", multiplier: 1024 * 1024 * 1024},
|
|
{suffix: "MB", multiplier: 1024 * 1024},
|
|
{suffix: "KB", multiplier: 1024},
|
|
{suffix: "B", multiplier: 1},
|
|
}
|
|
for _, unit := range units {
|
|
if value%unit.multiplier == 0 {
|
|
return strconv.FormatInt(value/unit.multiplier, 10) + unit.suffix
|
|
}
|
|
}
|
|
return strconv.FormatInt(value, 10) + "B"
|
|
}
|
|
|
|
func ParseByteSize(raw string) (ByteSize, error) {
|
|
value := strings.TrimSpace(raw)
|
|
if value == "" {
|
|
return 0, fmt.Errorf("size is required")
|
|
}
|
|
|
|
units := []struct {
|
|
suffix string
|
|
multiplier int64
|
|
}{
|
|
{suffix: "GB", multiplier: 1024 * 1024 * 1024},
|
|
{suffix: "MB", multiplier: 1024 * 1024},
|
|
{suffix: "KB", multiplier: 1024},
|
|
{suffix: "B", multiplier: 1},
|
|
}
|
|
for _, unit := range units {
|
|
number, ok := strings.CutSuffix(value, unit.suffix)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if strings.TrimSpace(number) != number || number == "" {
|
|
return 0, fmt.Errorf("size must be an integer followed by B, KB, MB, or GB")
|
|
}
|
|
parsed, err := strconv.ParseInt(number, 10, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("size must be an integer followed by B, KB, MB, or GB")
|
|
}
|
|
if parsed < 0 {
|
|
return 0, fmt.Errorf("size must be non-negative")
|
|
}
|
|
const maxInt64 = int64(1<<63 - 1)
|
|
if parsed > 0 && parsed > maxInt64/unit.multiplier {
|
|
return 0, fmt.Errorf("size is too large")
|
|
}
|
|
return ByteSize(parsed * unit.multiplier), nil
|
|
}
|
|
return 0, fmt.Errorf("size must use B, KB, MB, or GB suffix")
|
|
}
|
|
|
|
func (duration *Duration) UnmarshalYAML(value *yaml.Node) error {
|
|
if value.Kind != yaml.ScalarNode || value.Tag != "!!str" {
|
|
return fmt.Errorf("duration must be a string duration")
|
|
}
|
|
|
|
var raw string
|
|
if err := value.Decode(&raw); err != nil {
|
|
return err
|
|
}
|
|
parsed, err := time.ParseDuration(raw)
|
|
if err != nil {
|
|
return fmt.Errorf("duration must be a valid duration: %w", err)
|
|
}
|
|
*duration = Duration(parsed)
|
|
return nil
|
|
}
|
|
|
|
func (duration Duration) String() string {
|
|
return time.Duration(duration).String()
|
|
}
|
|
|
|
func (duration Duration) AsDuration() time.Duration {
|
|
return time.Duration(duration)
|
|
}
|