mirror of
https://github.com/zrepl/zrepl.git
synced 2024-11-28 11:25:20 +01:00
3ffb69bfb0
Originally, I had a patch that would replace all usages of time.Duration in package config with the new config.Duration types, but: 1. these are all timeouts/retry intervals that have default values. Most users don't touch them, and if they do, they don't need day or week units. 2. go-yaml's error reporting for yaml.Unmarshaler is inferior to built-in types (line numbers are missing, so the error would not have sufficient context) fixes https://github.com/zrepl/zrepl/issues/486
107 lines
2.3 KiB
Go
107 lines
2.3 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/kr/pretty"
|
|
"github.com/zrepl/yaml-config"
|
|
)
|
|
|
|
type Duration struct{ d time.Duration }
|
|
|
|
func (d Duration) Duration() time.Duration { return d.d }
|
|
|
|
var _ yaml.Unmarshaler = &Duration{}
|
|
|
|
func (d *Duration) UnmarshalYAML(unmarshal func(v interface{}, not_strict bool) error) error {
|
|
var s string
|
|
err := unmarshal(&s, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
d.d, err = parseDuration(s)
|
|
if err != nil {
|
|
d.d = 0
|
|
return &yaml.TypeError{Errors: []string{fmt.Sprintf("cannot parse value %q: %s", s, err)}}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type PositiveDuration struct{ d Duration }
|
|
|
|
var _ yaml.Unmarshaler = &PositiveDuration{}
|
|
|
|
func (d PositiveDuration) Duration() time.Duration { return d.d.Duration() }
|
|
|
|
func (d *PositiveDuration) UnmarshalYAML(unmarshal func(v interface{}, not_strict bool) error) error {
|
|
err := d.d.UnmarshalYAML(unmarshal)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if d.d.Duration() <= 0 {
|
|
return fmt.Errorf("duration must be positive, got %s", d.d.Duration())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parsePositiveDuration(e string) (time.Duration, error) {
|
|
d, err := parseDuration(e)
|
|
if err != nil {
|
|
return d, err
|
|
}
|
|
if d <= 0 {
|
|
return 0, errors.New("duration must be positive integer")
|
|
}
|
|
return d, err
|
|
}
|
|
|
|
var durationStringRegex *regexp.Regexp = regexp.MustCompile(`^\s*([\+-]?\d+)\s*(|s|m|h|d|w)\s*$`)
|
|
|
|
func parseDuration(e string) (d time.Duration, err error) {
|
|
comps := durationStringRegex.FindStringSubmatch(e)
|
|
if comps == nil {
|
|
err = fmt.Errorf("must match %s", durationStringRegex)
|
|
return
|
|
}
|
|
if len(comps) != 3 {
|
|
panic(pretty.Sprint(comps))
|
|
}
|
|
|
|
durationFactor, err := strconv.ParseInt(comps[1], 10, 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var durationUnit time.Duration
|
|
switch comps[2] {
|
|
case "":
|
|
if durationFactor != 0 {
|
|
err = fmt.Errorf("missing time unit")
|
|
return
|
|
} else {
|
|
// It's the case where user specified '0'.
|
|
// We want to allow this, just like time.ParseDuration.
|
|
}
|
|
case "s":
|
|
durationUnit = time.Second
|
|
case "m":
|
|
durationUnit = time.Minute
|
|
case "h":
|
|
durationUnit = time.Hour
|
|
case "d":
|
|
durationUnit = 24 * time.Hour
|
|
case "w":
|
|
durationUnit = 24 * 7 * time.Hour
|
|
default:
|
|
err = fmt.Errorf("contains unknown time unit '%s'", comps[2])
|
|
return
|
|
}
|
|
|
|
d = time.Duration(durationFactor) * durationUnit
|
|
return
|
|
}
|