rclone/fs/countsuffix.go
Nick Craig-Wood e43b5ce5e5 Remove github.com/pkg/errors and replace with std library version
This is possible now that we no longer support go1.12 and brings
rclone into line with standard practices in the Go world.

This also removes errors.New and errors.Errorf from lib/errors and
prefers the stdlib errors package over lib/errors.
2021-11-07 11:53:30 +00:00

189 lines
4.4 KiB
Go

package fs
// CountSuffix is parsed by flag with k/M/G decimal suffixes
import (
"errors"
"fmt"
"math"
"sort"
"strconv"
"strings"
)
// CountSuffix is an int64 with a friendly way of printing setting
type CountSuffix int64
// Common multipliers for SizeSuffix
const (
CountSuffixBase CountSuffix = 1
Kilo = 1000 * CountSuffixBase
Mega = 1000 * Kilo
Giga = 1000 * Mega
Tera = 1000 * Giga
Peta = 1000 * Tera
Exa = 1000 * Peta
)
const (
// CountSuffixMax is the largest CountSuffix multiplier
CountSuffixMax = Exa
// CountSuffixMaxValue is the largest value that can be used to create CountSuffix
CountSuffixMaxValue = math.MaxInt64
// CountSuffixMinValue is the smallest value that can be used to create CountSuffix
CountSuffixMinValue = math.MinInt64
)
// Turn CountSuffix into a string and a suffix
func (x CountSuffix) string() (string, string) {
scaled := float64(0)
suffix := ""
switch {
case x < 0:
return "off", ""
case x == 0:
return "0", ""
case x < Kilo:
scaled = float64(x)
suffix = ""
case x < Mega:
scaled = float64(x) / float64(Kilo)
suffix = "k"
case x < Giga:
scaled = float64(x) / float64(Mega)
suffix = "M"
case x < Tera:
scaled = float64(x) / float64(Giga)
suffix = "G"
case x < Peta:
scaled = float64(x) / float64(Tera)
suffix = "T"
case x < Exa:
scaled = float64(x) / float64(Peta)
suffix = "P"
default:
scaled = float64(x) / float64(Exa)
suffix = "E"
}
if math.Floor(scaled) == scaled {
return fmt.Sprintf("%.0f", scaled), suffix
}
return fmt.Sprintf("%.3f", scaled), suffix
}
// String turns CountSuffix into a string
func (x CountSuffix) String() string {
val, suffix := x.string()
return val + suffix
}
// Unit turns CountSuffix into a string with a unit
func (x CountSuffix) Unit(unit string) string {
val, suffix := x.string()
if val == "off" {
return val
}
var suffixUnit string
if suffix != "" && unit != "" {
suffixUnit = suffix + unit
} else {
suffixUnit = suffix + unit
}
return val + " " + suffixUnit
}
func (x *CountSuffix) multiplierFromSymbol(s byte) (found bool, multiplier float64) {
switch s {
case 'k', 'K':
return true, float64(Kilo)
case 'm', 'M':
return true, float64(Mega)
case 'g', 'G':
return true, float64(Giga)
case 't', 'T':
return true, float64(Tera)
case 'p', 'P':
return true, float64(Peta)
case 'e', 'E':
return true, float64(Exa)
default:
return false, float64(CountSuffixBase)
}
}
// Set a CountSuffix
func (x *CountSuffix) Set(s string) error {
if len(s) == 0 {
return errors.New("empty string")
}
if strings.ToLower(s) == "off" {
*x = -1
return nil
}
suffix := s[len(s)-1]
suffixLen := 1
multiplierFound := false
var multiplier float64
switch suffix {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
suffixLen = 0
multiplier = float64(Kilo)
case 'b', 'B':
if len(s) > 1 {
suffix = s[len(s)-2]
if multiplierFound, multiplier = x.multiplierFromSymbol(suffix); multiplierFound {
suffixLen = 2
}
} else {
multiplier = float64(CountSuffixBase)
}
default:
if multiplierFound, multiplier = x.multiplierFromSymbol(suffix); !multiplierFound {
return fmt.Errorf("bad suffix %q", suffix)
}
}
s = s[:len(s)-suffixLen]
value, err := strconv.ParseFloat(s, 64)
if err != nil {
return err
}
if value < 0 {
return fmt.Errorf("size can't be negative %q", s)
}
value *= multiplier
*x = CountSuffix(value)
return nil
}
// Type of the value
func (x *CountSuffix) Type() string {
return "CountSuffix"
}
// Scan implements the fmt.Scanner interface
func (x *CountSuffix) Scan(s fmt.ScanState, ch rune) error {
token, err := s.Token(true, nil)
if err != nil {
return err
}
return x.Set(string(token))
}
// CountSuffixList is a slice CountSuffix values
type CountSuffixList []CountSuffix
func (l CountSuffixList) Len() int { return len(l) }
func (l CountSuffixList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l CountSuffixList) Less(i, j int) bool { return l[i] < l[j] }
// Sort sorts the list
func (l CountSuffixList) Sort() {
sort.Sort(l)
}
// UnmarshalJSON makes sure the value can be parsed as a string or integer in JSON
func (x *CountSuffix) UnmarshalJSON(in []byte) error {
return UnmarshalJSONFlag(in, x, func(i int64) error {
*x = CountSuffix(i)
return nil
})
}