// Copyright (C) 2018 The Syncthing Authors. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. package main import ( "bytes" "fmt" "strings" ) type NumberType int const ( NumberMetric NumberType = iota NumberBinary NumberDuration ) func number(ntype NumberType, v float64) string { switch ntype { case NumberMetric: return metric(v) case NumberDuration: return duration(v) case NumberBinary: return binary(v) default: return metric(v) } } type suffix struct { Suffix string Multiplier float64 } var metricSuffixes = []suffix{ {"G", 1e9}, {"M", 1e6}, {"k", 1e3}, } var binarySuffixes = []suffix{ {"Gi", 1 << 30}, {"Mi", 1 << 20}, {"Ki", 1 << 10}, } var durationSuffix = []suffix{ {"year", 365 * 24 * 60 * 60}, {"month", 30 * 24 * 60 * 60}, {"day", 24 * 60 * 60}, {"hour", 60 * 60}, {"minute", 60}, {"second", 1}, } func metric(v float64) string { return withSuffix(v, metricSuffixes, false) } func binary(v float64) string { return withSuffix(v, binarySuffixes, false) } func duration(v float64) string { return withSuffix(v, durationSuffix, true) } func withSuffix(v float64, ps []suffix, pluralize bool) string { for _, p := range ps { if v >= p.Multiplier { suffix := p.Suffix if pluralize && v/p.Multiplier != 1.0 { suffix += "s" } // If the number only has decimal zeroes, strip em off. num := strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.1f", v/p.Multiplier), "0"), ".") return fmt.Sprintf("%s %s", num, suffix) } } return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.1f", v), "0"), ".") } // commatize returns a number with sep as thousands separators. Handles // integers and plain floats. func commatize(sep, s string) string { // If no dot, don't do anything. if !strings.ContainsRune(s, '.') { return s } var b bytes.Buffer fs := strings.SplitN(s, ".", 2) l := len(fs[0]) for i := range fs[0] { b.Write([]byte{s[i]}) if i < l-1 && (l-i)%3 == 1 { b.WriteString(sep) } } if len(fs) > 1 && len(fs[1]) > 0 { b.WriteString(".") b.WriteString(fs[1]) } return b.String() } func proportion(m map[string]int, count int) float64 { total := 0 isMax := true for _, n := range m { total += n if n > count { isMax = false } } pct := (100 * float64(count)) / float64(total) // To avoid rounding errors in the template, surpassing 100% and breaking // the progress bars. if isMax && len(m) > 1 && count != total { pct -= 0.01 } return pct }