rclone/fs/countsuffix.go
Nick Craig-Wood 3553cc4a5f fs: fix option types printing incorrectly for backend flags
Before this change backend types were printing incorrectly as the name
of the type, not what was defined by the Type() method.

This was not working due to not calling the Type() method. However
this needed to be defined on a non-pointer type due to the way the
options are handled.
2023-10-03 11:23:58 +01:00

188 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
})
}