package restic import ( "fmt" "strconv" "strings" "unicode" "github.com/restic/restic/internal/errors" ) // Duration is similar to time.Duration, except it only supports larger ranges // like hours, days, months, and years. type Duration struct { Hours, Days, Months, Years int } func (d Duration) String() string { var s string if d.Years != 0 { s += fmt.Sprintf("%dy", d.Years) } if d.Months != 0 { s += fmt.Sprintf("%dm", d.Months) } if d.Days != 0 { s += fmt.Sprintf("%dd", d.Days) } if d.Hours != 0 { s += fmt.Sprintf("%dh", d.Hours) } return s } func nextNumber(input string) (num int, rest string, err error) { if len(input) == 0 { return 0, "", nil } var ( n string negative bool ) if input[0] == '-' { negative = true input = input[1:] } for i, s := range input { if !unicode.IsNumber(s) { rest = input[i:] break } n += string(s) } if len(n) == 0 { return 0, input, errors.New("no number found") } num, err = strconv.Atoi(n) if err != nil { panic(err) } if negative { num = -num } return num, rest, nil } // ParseDuration parses a duration from a string. The format is `6y5m234d37h` func ParseDuration(s string) (Duration, error) { var ( d Duration num int err error ) s = strings.TrimSpace(s) for s != "" { num, s, err = nextNumber(s) if err != nil { return Duration{}, err } if len(s) == 0 { return Duration{}, errors.Errorf("no unit found after number %d", num) } switch s[0] { case 'y': d.Years = num case 'm': d.Months = num case 'd': d.Days = num case 'h': d.Hours = num default: return Duration{}, errors.Errorf("invalid unit %q found after number %d", s[0], num) } s = s[1:] } return d, nil } // Set calls ParseDuration and updates d. func (d *Duration) Set(s string) error { v, err := ParseDuration(s) if err != nil { return err } *d = v return nil } // Type returns the type of Duration, usable within github.com/spf13/pflag and // in help texts. func (d Duration) Type() string { return "duration" } // Zero returns true if the duration is empty (all values are set to zero). func (d Duration) Zero() bool { return d.Years == 0 && d.Months == 0 && d.Days == 0 && d.Hours == 0 }