diff options
Diffstat (limited to 'vendor/github.com/dustin/go-humanize')
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/.travis.yml | 21 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/LICENSE | 21 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/README.markdown | 124 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/big.go | 31 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/bigbytes.go | 189 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/bytes.go | 143 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/comma.go | 116 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/commaf.go | 41 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/ftoa.go | 49 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/humanize.go | 8 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/number.go | 192 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/ordinals.go | 25 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/si.go | 127 | ||||
| -rw-r--r-- | vendor/github.com/dustin/go-humanize/times.go | 117 | 
14 files changed, 1204 insertions, 0 deletions
| diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml new file mode 100644 index 0000000..ac12e48 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/.travis.yml | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | sudo: false | ||
| 2 | language: go | ||
| 3 | go_import_path: github.com/dustin/go-humanize | ||
| 4 | go: | ||
| 5 | - 1.13.x | ||
| 6 | - 1.14.x | ||
| 7 | - 1.15.x | ||
| 8 | - 1.16.x | ||
| 9 | - stable | ||
| 10 | - master | ||
| 11 | matrix: | ||
| 12 | allow_failures: | ||
| 13 | - go: master | ||
| 14 | fast_finish: true | ||
| 15 | install: | ||
| 16 | - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). | ||
| 17 | script: | ||
| 18 | - diff -u <(echo -n) <(gofmt -d -s .) | ||
| 19 | - go vet . | ||
| 20 | - go install -v -race ./... | ||
| 21 | - go test -v -race ./... | ||
| diff --git a/vendor/github.com/dustin/go-humanize/LICENSE b/vendor/github.com/dustin/go-humanize/LICENSE new file mode 100644 index 0000000..8d9a94a --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/LICENSE | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | Copyright (c) 2005-2008 Dustin Sallings <[email protected]> | ||
| 2 | |||
| 3 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 4 | of this software and associated documentation files (the "Software"), to deal | ||
| 5 | in the Software without restriction, including without limitation the rights | ||
| 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | copies of the Software, and to permit persons to whom the Software is | ||
| 8 | furnished to do so, subject to the following conditions: | ||
| 9 | |||
| 10 | The above copyright notice and this permission notice shall be included in | ||
| 11 | all copies or substantial portions of the Software. | ||
| 12 | |||
| 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 19 | SOFTWARE. | ||
| 20 | |||
| 21 | <http://www.opensource.org/licenses/mit-license.php> | ||
| diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown new file mode 100644 index 0000000..7d0b16b --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/README.markdown | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | # Humane Units [](https://travis-ci.org/dustin/go-humanize) [](https://godoc.org/github.com/dustin/go-humanize) | ||
| 2 | |||
| 3 | Just a few functions for helping humanize times and sizes. | ||
| 4 | |||
| 5 | `go get` it as `github.com/dustin/go-humanize`, import it as | ||
| 6 | `"github.com/dustin/go-humanize"`, use it as `humanize`. | ||
| 7 | |||
| 8 | See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for | ||
| 9 | complete documentation. | ||
| 10 | |||
| 11 | ## Sizes | ||
| 12 | |||
| 13 | This lets you take numbers like `82854982` and convert them to useful | ||
| 14 | strings like, `83 MB` or `79 MiB` (whichever you prefer). | ||
| 15 | |||
| 16 | Example: | ||
| 17 | |||
| 18 | ```go | ||
| 19 | fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. | ||
| 20 | ``` | ||
| 21 | |||
| 22 | ## Times | ||
| 23 | |||
| 24 | This lets you take a `time.Time` and spit it out in relative terms. | ||
| 25 | For example, `12 seconds ago` or `3 days from now`. | ||
| 26 | |||
| 27 | Example: | ||
| 28 | |||
| 29 | ```go | ||
| 30 | fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. | ||
| 31 | ``` | ||
| 32 | |||
| 33 | Thanks to Kyle Lemons for the time implementation from an IRC | ||
| 34 | conversation one day. It's pretty neat. | ||
| 35 | |||
| 36 | ## Ordinals | ||
| 37 | |||
| 38 | From a [mailing list discussion][odisc] where a user wanted to be able | ||
| 39 | to label ordinals. | ||
| 40 | |||
| 41 | 0 -> 0th | ||
| 42 | 1 -> 1st | ||
| 43 | 2 -> 2nd | ||
| 44 | 3 -> 3rd | ||
| 45 | 4 -> 4th | ||
| 46 | [...] | ||
| 47 | |||
| 48 | Example: | ||
| 49 | |||
| 50 | ```go | ||
| 51 | fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. | ||
| 52 | ``` | ||
| 53 | |||
| 54 | ## Commas | ||
| 55 | |||
| 56 | Want to shove commas into numbers? Be my guest. | ||
| 57 | |||
| 58 | 0 -> 0 | ||
| 59 | 100 -> 100 | ||
| 60 | 1000 -> 1,000 | ||
| 61 | 1000000000 -> 1,000,000,000 | ||
| 62 | -100000 -> -100,000 | ||
| 63 | |||
| 64 | Example: | ||
| 65 | |||
| 66 | ```go | ||
| 67 | fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. | ||
| 68 | ``` | ||
| 69 | |||
| 70 | ## Ftoa | ||
| 71 | |||
| 72 | Nicer float64 formatter that removes trailing zeros. | ||
| 73 | |||
| 74 | ```go | ||
| 75 | fmt.Printf("%f", 2.24) // 2.240000 | ||
| 76 | fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 | ||
| 77 | fmt.Printf("%f", 2.0) // 2.000000 | ||
| 78 | fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 | ||
| 79 | ``` | ||
| 80 | |||
| 81 | ## SI notation | ||
| 82 | |||
| 83 | Format numbers with [SI notation][sinotation]. | ||
| 84 | |||
| 85 | Example: | ||
| 86 | |||
| 87 | ```go | ||
| 88 | humanize.SI(0.00000000223, "M") // 2.23 nM | ||
| 89 | ``` | ||
| 90 | |||
| 91 | ## English-specific functions | ||
| 92 | |||
| 93 | The following functions are in the `humanize/english` subpackage. | ||
| 94 | |||
| 95 | ### Plurals | ||
| 96 | |||
| 97 | Simple English pluralization | ||
| 98 | |||
| 99 | ```go | ||
| 100 | english.PluralWord(1, "object", "") // object | ||
| 101 | english.PluralWord(42, "object", "") // objects | ||
| 102 | english.PluralWord(2, "bus", "") // buses | ||
| 103 | english.PluralWord(99, "locus", "loci") // loci | ||
| 104 | |||
| 105 | english.Plural(1, "object", "") // 1 object | ||
| 106 | english.Plural(42, "object", "") // 42 objects | ||
| 107 | english.Plural(2, "bus", "") // 2 buses | ||
| 108 | english.Plural(99, "locus", "loci") // 99 loci | ||
| 109 | ``` | ||
| 110 | |||
| 111 | ### Word series | ||
| 112 | |||
| 113 | Format comma-separated words lists with conjuctions: | ||
| 114 | |||
| 115 | ```go | ||
| 116 | english.WordSeries([]string{"foo"}, "and") // foo | ||
| 117 | english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar | ||
| 118 | english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz | ||
| 119 | |||
| 120 | english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz | ||
| 121 | ``` | ||
| 122 | |||
| 123 | [odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion | ||
| 124 | [sinotation]: http://en.wikipedia.org/wiki/Metric_prefix | ||
| diff --git a/vendor/github.com/dustin/go-humanize/big.go b/vendor/github.com/dustin/go-humanize/big.go new file mode 100644 index 0000000..f49dc33 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/big.go | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "math/big" | ||
| 5 | ) | ||
| 6 | |||
| 7 | // order of magnitude (to a max order) | ||
| 8 | func oomm(n, b *big.Int, maxmag int) (float64, int) { | ||
| 9 | mag := 0 | ||
| 10 | m := &big.Int{} | ||
| 11 | for n.Cmp(b) >= 0 { | ||
| 12 | n.DivMod(n, b, m) | ||
| 13 | mag++ | ||
| 14 | if mag == maxmag && maxmag >= 0 { | ||
| 15 | break | ||
| 16 | } | ||
| 17 | } | ||
| 18 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag | ||
| 19 | } | ||
| 20 | |||
| 21 | // total order of magnitude | ||
| 22 | // (same as above, but with no upper limit) | ||
| 23 | func oom(n, b *big.Int) (float64, int) { | ||
| 24 | mag := 0 | ||
| 25 | m := &big.Int{} | ||
| 26 | for n.Cmp(b) >= 0 { | ||
| 27 | n.DivMod(n, b, m) | ||
| 28 | mag++ | ||
| 29 | } | ||
| 30 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag | ||
| 31 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go new file mode 100644 index 0000000..3b015fd --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "math/big" | ||
| 6 | "strings" | ||
| 7 | "unicode" | ||
| 8 | ) | ||
| 9 | |||
| 10 | var ( | ||
| 11 | bigIECExp = big.NewInt(1024) | ||
| 12 | |||
| 13 | // BigByte is one byte in bit.Ints | ||
| 14 | BigByte = big.NewInt(1) | ||
| 15 | // BigKiByte is 1,024 bytes in bit.Ints | ||
| 16 | BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) | ||
| 17 | // BigMiByte is 1,024 k bytes in bit.Ints | ||
| 18 | BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) | ||
| 19 | // BigGiByte is 1,024 m bytes in bit.Ints | ||
| 20 | BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) | ||
| 21 | // BigTiByte is 1,024 g bytes in bit.Ints | ||
| 22 | BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) | ||
| 23 | // BigPiByte is 1,024 t bytes in bit.Ints | ||
| 24 | BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) | ||
| 25 | // BigEiByte is 1,024 p bytes in bit.Ints | ||
| 26 | BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) | ||
| 27 | // BigZiByte is 1,024 e bytes in bit.Ints | ||
| 28 | BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) | ||
| 29 | // BigYiByte is 1,024 z bytes in bit.Ints | ||
| 30 | BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) | ||
| 31 | // BigRiByte is 1,024 y bytes in bit.Ints | ||
| 32 | BigRiByte = (&big.Int{}).Mul(BigYiByte, bigIECExp) | ||
| 33 | // BigQiByte is 1,024 r bytes in bit.Ints | ||
| 34 | BigQiByte = (&big.Int{}).Mul(BigRiByte, bigIECExp) | ||
| 35 | ) | ||
| 36 | |||
| 37 | var ( | ||
| 38 | bigSIExp = big.NewInt(1000) | ||
| 39 | |||
| 40 | // BigSIByte is one SI byte in big.Ints | ||
| 41 | BigSIByte = big.NewInt(1) | ||
| 42 | // BigKByte is 1,000 SI bytes in big.Ints | ||
| 43 | BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) | ||
| 44 | // BigMByte is 1,000 SI k bytes in big.Ints | ||
| 45 | BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) | ||
| 46 | // BigGByte is 1,000 SI m bytes in big.Ints | ||
| 47 | BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) | ||
| 48 | // BigTByte is 1,000 SI g bytes in big.Ints | ||
| 49 | BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) | ||
| 50 | // BigPByte is 1,000 SI t bytes in big.Ints | ||
| 51 | BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) | ||
| 52 | // BigEByte is 1,000 SI p bytes in big.Ints | ||
| 53 | BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) | ||
| 54 | // BigZByte is 1,000 SI e bytes in big.Ints | ||
| 55 | BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) | ||
| 56 | // BigYByte is 1,000 SI z bytes in big.Ints | ||
| 57 | BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) | ||
| 58 | // BigRByte is 1,000 SI y bytes in big.Ints | ||
| 59 | BigRByte = (&big.Int{}).Mul(BigYByte, bigSIExp) | ||
| 60 | // BigQByte is 1,000 SI r bytes in big.Ints | ||
| 61 | BigQByte = (&big.Int{}).Mul(BigRByte, bigSIExp) | ||
| 62 | ) | ||
| 63 | |||
| 64 | var bigBytesSizeTable = map[string]*big.Int{ | ||
| 65 | "b": BigByte, | ||
| 66 | "kib": BigKiByte, | ||
| 67 | "kb": BigKByte, | ||
| 68 | "mib": BigMiByte, | ||
| 69 | "mb": BigMByte, | ||
| 70 | "gib": BigGiByte, | ||
| 71 | "gb": BigGByte, | ||
| 72 | "tib": BigTiByte, | ||
| 73 | "tb": BigTByte, | ||
| 74 | "pib": BigPiByte, | ||
| 75 | "pb": BigPByte, | ||
| 76 | "eib": BigEiByte, | ||
| 77 | "eb": BigEByte, | ||
| 78 | "zib": BigZiByte, | ||
| 79 | "zb": BigZByte, | ||
| 80 | "yib": BigYiByte, | ||
| 81 | "yb": BigYByte, | ||
| 82 | "rib": BigRiByte, | ||
| 83 | "rb": BigRByte, | ||
| 84 | "qib": BigQiByte, | ||
| 85 | "qb": BigQByte, | ||
| 86 | // Without suffix | ||
| 87 | "": BigByte, | ||
| 88 | "ki": BigKiByte, | ||
| 89 | "k": BigKByte, | ||
| 90 | "mi": BigMiByte, | ||
| 91 | "m": BigMByte, | ||
| 92 | "gi": BigGiByte, | ||
| 93 | "g": BigGByte, | ||
| 94 | "ti": BigTiByte, | ||
| 95 | "t": BigTByte, | ||
| 96 | "pi": BigPiByte, | ||
| 97 | "p": BigPByte, | ||
| 98 | "ei": BigEiByte, | ||
| 99 | "e": BigEByte, | ||
| 100 | "z": BigZByte, | ||
| 101 | "zi": BigZiByte, | ||
| 102 | "y": BigYByte, | ||
| 103 | "yi": BigYiByte, | ||
| 104 | "r": BigRByte, | ||
| 105 | "ri": BigRiByte, | ||
| 106 | "q": BigQByte, | ||
| 107 | "qi": BigQiByte, | ||
| 108 | } | ||
| 109 | |||
| 110 | var ten = big.NewInt(10) | ||
| 111 | |||
| 112 | func humanateBigBytes(s, base *big.Int, sizes []string) string { | ||
| 113 | if s.Cmp(ten) < 0 { | ||
| 114 | return fmt.Sprintf("%d B", s) | ||
| 115 | } | ||
| 116 | c := (&big.Int{}).Set(s) | ||
| 117 | val, mag := oomm(c, base, len(sizes)-1) | ||
| 118 | suffix := sizes[mag] | ||
| 119 | f := "%.0f %s" | ||
| 120 | if val < 10 { | ||
| 121 | f = "%.1f %s" | ||
| 122 | } | ||
| 123 | |||
| 124 | return fmt.Sprintf(f, val, suffix) | ||
| 125 | |||
| 126 | } | ||
| 127 | |||
| 128 | // BigBytes produces a human readable representation of an SI size. | ||
| 129 | // | ||
| 130 | // See also: ParseBigBytes. | ||
| 131 | // | ||
| 132 | // BigBytes(82854982) -> 83 MB | ||
| 133 | func BigBytes(s *big.Int) string { | ||
| 134 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB"} | ||
| 135 | return humanateBigBytes(s, bigSIExp, sizes) | ||
| 136 | } | ||
| 137 | |||
| 138 | // BigIBytes produces a human readable representation of an IEC size. | ||
| 139 | // | ||
| 140 | // See also: ParseBigBytes. | ||
| 141 | // | ||
| 142 | // BigIBytes(82854982) -> 79 MiB | ||
| 143 | func BigIBytes(s *big.Int) string { | ||
| 144 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"} | ||
| 145 | return humanateBigBytes(s, bigIECExp, sizes) | ||
| 146 | } | ||
| 147 | |||
| 148 | // ParseBigBytes parses a string representation of bytes into the number | ||
| 149 | // of bytes it represents. | ||
| 150 | // | ||
| 151 | // See also: BigBytes, BigIBytes. | ||
| 152 | // | ||
| 153 | // ParseBigBytes("42 MB") -> 42000000, nil | ||
| 154 | // ParseBigBytes("42 mib") -> 44040192, nil | ||
| 155 | func ParseBigBytes(s string) (*big.Int, error) { | ||
| 156 | lastDigit := 0 | ||
| 157 | hasComma := false | ||
| 158 | for _, r := range s { | ||
| 159 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { | ||
| 160 | break | ||
| 161 | } | ||
| 162 | if r == ',' { | ||
| 163 | hasComma = true | ||
| 164 | } | ||
| 165 | lastDigit++ | ||
| 166 | } | ||
| 167 | |||
| 168 | num := s[:lastDigit] | ||
| 169 | if hasComma { | ||
| 170 | num = strings.Replace(num, ",", "", -1) | ||
| 171 | } | ||
| 172 | |||
| 173 | val := &big.Rat{} | ||
| 174 | _, err := fmt.Sscanf(num, "%f", val) | ||
| 175 | if err != nil { | ||
| 176 | return nil, err | ||
| 177 | } | ||
| 178 | |||
| 179 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) | ||
| 180 | if m, ok := bigBytesSizeTable[extra]; ok { | ||
| 181 | mv := (&big.Rat{}).SetInt(m) | ||
| 182 | val.Mul(val, mv) | ||
| 183 | rv := &big.Int{} | ||
| 184 | rv.Div(val.Num(), val.Denom()) | ||
| 185 | return rv, nil | ||
| 186 | } | ||
| 187 | |||
| 188 | return nil, fmt.Errorf("unhandled size name: %v", extra) | ||
| 189 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/bytes.go b/vendor/github.com/dustin/go-humanize/bytes.go new file mode 100644 index 0000000..0b498f4 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bytes.go | |||
| @@ -0,0 +1,143 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "math" | ||
| 6 | "strconv" | ||
| 7 | "strings" | ||
| 8 | "unicode" | ||
| 9 | ) | ||
| 10 | |||
| 11 | // IEC Sizes. | ||
| 12 | // kibis of bits | ||
| 13 | const ( | ||
| 14 | Byte = 1 << (iota * 10) | ||
| 15 | KiByte | ||
| 16 | MiByte | ||
| 17 | GiByte | ||
| 18 | TiByte | ||
| 19 | PiByte | ||
| 20 | EiByte | ||
| 21 | ) | ||
| 22 | |||
| 23 | // SI Sizes. | ||
| 24 | const ( | ||
| 25 | IByte = 1 | ||
| 26 | KByte = IByte * 1000 | ||
| 27 | MByte = KByte * 1000 | ||
| 28 | GByte = MByte * 1000 | ||
| 29 | TByte = GByte * 1000 | ||
| 30 | PByte = TByte * 1000 | ||
| 31 | EByte = PByte * 1000 | ||
| 32 | ) | ||
| 33 | |||
| 34 | var bytesSizeTable = map[string]uint64{ | ||
| 35 | "b": Byte, | ||
| 36 | "kib": KiByte, | ||
| 37 | "kb": KByte, | ||
| 38 | "mib": MiByte, | ||
| 39 | "mb": MByte, | ||
| 40 | "gib": GiByte, | ||
| 41 | "gb": GByte, | ||
| 42 | "tib": TiByte, | ||
| 43 | "tb": TByte, | ||
| 44 | "pib": PiByte, | ||
| 45 | "pb": PByte, | ||
| 46 | "eib": EiByte, | ||
| 47 | "eb": EByte, | ||
| 48 | // Without suffix | ||
| 49 | "": Byte, | ||
| 50 | "ki": KiByte, | ||
| 51 | "k": KByte, | ||
| 52 | "mi": MiByte, | ||
| 53 | "m": MByte, | ||
| 54 | "gi": GiByte, | ||
| 55 | "g": GByte, | ||
| 56 | "ti": TiByte, | ||
| 57 | "t": TByte, | ||
| 58 | "pi": PiByte, | ||
| 59 | "p": PByte, | ||
| 60 | "ei": EiByte, | ||
| 61 | "e": EByte, | ||
| 62 | } | ||
| 63 | |||
| 64 | func logn(n, b float64) float64 { | ||
| 65 | return math.Log(n) / math.Log(b) | ||
| 66 | } | ||
| 67 | |||
| 68 | func humanateBytes(s uint64, base float64, sizes []string) string { | ||
| 69 | if s < 10 { | ||
| 70 | return fmt.Sprintf("%d B", s) | ||
| 71 | } | ||
| 72 | e := math.Floor(logn(float64(s), base)) | ||
| 73 | suffix := sizes[int(e)] | ||
| 74 | val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 | ||
| 75 | f := "%.0f %s" | ||
| 76 | if val < 10 { | ||
| 77 | f = "%.1f %s" | ||
| 78 | } | ||
| 79 | |||
| 80 | return fmt.Sprintf(f, val, suffix) | ||
| 81 | } | ||
| 82 | |||
| 83 | // Bytes produces a human readable representation of an SI size. | ||
| 84 | // | ||
| 85 | // See also: ParseBytes. | ||
| 86 | // | ||
| 87 | // Bytes(82854982) -> 83 MB | ||
| 88 | func Bytes(s uint64) string { | ||
| 89 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} | ||
| 90 | return humanateBytes(s, 1000, sizes) | ||
| 91 | } | ||
| 92 | |||
| 93 | // IBytes produces a human readable representation of an IEC size. | ||
| 94 | // | ||
| 95 | // See also: ParseBytes. | ||
| 96 | // | ||
| 97 | // IBytes(82854982) -> 79 MiB | ||
| 98 | func IBytes(s uint64) string { | ||
| 99 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} | ||
| 100 | return humanateBytes(s, 1024, sizes) | ||
| 101 | } | ||
| 102 | |||
| 103 | // ParseBytes parses a string representation of bytes into the number | ||
| 104 | // of bytes it represents. | ||
| 105 | // | ||
| 106 | // See Also: Bytes, IBytes. | ||
| 107 | // | ||
| 108 | // ParseBytes("42 MB") -> 42000000, nil | ||
| 109 | // ParseBytes("42 mib") -> 44040192, nil | ||
| 110 | func ParseBytes(s string) (uint64, error) { | ||
| 111 | lastDigit := 0 | ||
| 112 | hasComma := false | ||
| 113 | for _, r := range s { | ||
| 114 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { | ||
| 115 | break | ||
| 116 | } | ||
| 117 | if r == ',' { | ||
| 118 | hasComma = true | ||
| 119 | } | ||
| 120 | lastDigit++ | ||
| 121 | } | ||
| 122 | |||
| 123 | num := s[:lastDigit] | ||
| 124 | if hasComma { | ||
| 125 | num = strings.Replace(num, ",", "", -1) | ||
| 126 | } | ||
| 127 | |||
| 128 | f, err := strconv.ParseFloat(num, 64) | ||
| 129 | if err != nil { | ||
| 130 | return 0, err | ||
| 131 | } | ||
| 132 | |||
| 133 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) | ||
| 134 | if m, ok := bytesSizeTable[extra]; ok { | ||
| 135 | f *= float64(m) | ||
| 136 | if f >= math.MaxUint64 { | ||
| 137 | return 0, fmt.Errorf("too large: %v", s) | ||
| 138 | } | ||
| 139 | return uint64(f), nil | ||
| 140 | } | ||
| 141 | |||
| 142 | return 0, fmt.Errorf("unhandled size name: %v", extra) | ||
| 143 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/comma.go b/vendor/github.com/dustin/go-humanize/comma.go new file mode 100644 index 0000000..520ae3e --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/comma.go | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bytes" | ||
| 5 | "math" | ||
| 6 | "math/big" | ||
| 7 | "strconv" | ||
| 8 | "strings" | ||
| 9 | ) | ||
| 10 | |||
| 11 | // Comma produces a string form of the given number in base 10 with | ||
| 12 | // commas after every three orders of magnitude. | ||
| 13 | // | ||
| 14 | // e.g. Comma(834142) -> 834,142 | ||
| 15 | func Comma(v int64) string { | ||
| 16 | sign := "" | ||
| 17 | |||
| 18 | // Min int64 can't be negated to a usable value, so it has to be special cased. | ||
| 19 | if v == math.MinInt64 { | ||
| 20 | return "-9,223,372,036,854,775,808" | ||
| 21 | } | ||
| 22 | |||
| 23 | if v < 0 { | ||
| 24 | sign = "-" | ||
| 25 | v = 0 - v | ||
| 26 | } | ||
| 27 | |||
| 28 | parts := []string{"", "", "", "", "", "", ""} | ||
| 29 | j := len(parts) - 1 | ||
| 30 | |||
| 31 | for v > 999 { | ||
| 32 | parts[j] = strconv.FormatInt(v%1000, 10) | ||
| 33 | switch len(parts[j]) { | ||
| 34 | case 2: | ||
| 35 | parts[j] = "0" + parts[j] | ||
| 36 | case 1: | ||
| 37 | parts[j] = "00" + parts[j] | ||
| 38 | } | ||
| 39 | v = v / 1000 | ||
| 40 | j-- | ||
| 41 | } | ||
| 42 | parts[j] = strconv.Itoa(int(v)) | ||
| 43 | return sign + strings.Join(parts[j:], ",") | ||
| 44 | } | ||
| 45 | |||
| 46 | // Commaf produces a string form of the given number in base 10 with | ||
| 47 | // commas after every three orders of magnitude. | ||
| 48 | // | ||
| 49 | // e.g. Commaf(834142.32) -> 834,142.32 | ||
| 50 | func Commaf(v float64) string { | ||
| 51 | buf := &bytes.Buffer{} | ||
| 52 | if v < 0 { | ||
| 53 | buf.Write([]byte{'-'}) | ||
| 54 | v = 0 - v | ||
| 55 | } | ||
| 56 | |||
| 57 | comma := []byte{','} | ||
| 58 | |||
| 59 | parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") | ||
| 60 | pos := 0 | ||
| 61 | if len(parts[0])%3 != 0 { | ||
| 62 | pos += len(parts[0]) % 3 | ||
| 63 | buf.WriteString(parts[0][:pos]) | ||
| 64 | buf.Write(comma) | ||
| 65 | } | ||
| 66 | for ; pos < len(parts[0]); pos += 3 { | ||
| 67 | buf.WriteString(parts[0][pos : pos+3]) | ||
| 68 | buf.Write(comma) | ||
| 69 | } | ||
| 70 | buf.Truncate(buf.Len() - 1) | ||
| 71 | |||
| 72 | if len(parts) > 1 { | ||
| 73 | buf.Write([]byte{'.'}) | ||
| 74 | buf.WriteString(parts[1]) | ||
| 75 | } | ||
| 76 | return buf.String() | ||
| 77 | } | ||
| 78 | |||
| 79 | // CommafWithDigits works like the Commaf but limits the resulting | ||
| 80 | // string to the given number of decimal places. | ||
| 81 | // | ||
| 82 | // e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 | ||
| 83 | func CommafWithDigits(f float64, decimals int) string { | ||
| 84 | return stripTrailingDigits(Commaf(f), decimals) | ||
| 85 | } | ||
| 86 | |||
| 87 | // BigComma produces a string form of the given big.Int in base 10 | ||
| 88 | // with commas after every three orders of magnitude. | ||
| 89 | func BigComma(b *big.Int) string { | ||
| 90 | sign := "" | ||
| 91 | if b.Sign() < 0 { | ||
| 92 | sign = "-" | ||
| 93 | b.Abs(b) | ||
| 94 | } | ||
| 95 | |||
| 96 | athousand := big.NewInt(1000) | ||
| 97 | c := (&big.Int{}).Set(b) | ||
| 98 | _, m := oom(c, athousand) | ||
| 99 | parts := make([]string, m+1) | ||
| 100 | j := len(parts) - 1 | ||
| 101 | |||
| 102 | mod := &big.Int{} | ||
| 103 | for b.Cmp(athousand) >= 0 { | ||
| 104 | b.DivMod(b, athousand, mod) | ||
| 105 | parts[j] = strconv.FormatInt(mod.Int64(), 10) | ||
| 106 | switch len(parts[j]) { | ||
| 107 | case 2: | ||
| 108 | parts[j] = "0" + parts[j] | ||
| 109 | case 1: | ||
| 110 | parts[j] = "00" + parts[j] | ||
| 111 | } | ||
| 112 | j-- | ||
| 113 | } | ||
| 114 | parts[j] = strconv.Itoa(int(b.Int64())) | ||
| 115 | return sign + strings.Join(parts[j:], ",") | ||
| 116 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go new file mode 100644 index 0000000..2bc83a0 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/commaf.go | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | //go:build go1.6 | ||
| 2 | // +build go1.6 | ||
| 3 | |||
| 4 | package humanize | ||
| 5 | |||
| 6 | import ( | ||
| 7 | "bytes" | ||
| 8 | "math/big" | ||
| 9 | "strings" | ||
| 10 | ) | ||
| 11 | |||
| 12 | // BigCommaf produces a string form of the given big.Float in base 10 | ||
| 13 | // with commas after every three orders of magnitude. | ||
| 14 | func BigCommaf(v *big.Float) string { | ||
| 15 | buf := &bytes.Buffer{} | ||
| 16 | if v.Sign() < 0 { | ||
| 17 | buf.Write([]byte{'-'}) | ||
| 18 | v.Abs(v) | ||
| 19 | } | ||
| 20 | |||
| 21 | comma := []byte{','} | ||
| 22 | |||
| 23 | parts := strings.Split(v.Text('f', -1), ".") | ||
| 24 | pos := 0 | ||
| 25 | if len(parts[0])%3 != 0 { | ||
| 26 | pos += len(parts[0]) % 3 | ||
| 27 | buf.WriteString(parts[0][:pos]) | ||
| 28 | buf.Write(comma) | ||
| 29 | } | ||
| 30 | for ; pos < len(parts[0]); pos += 3 { | ||
| 31 | buf.WriteString(parts[0][pos : pos+3]) | ||
| 32 | buf.Write(comma) | ||
| 33 | } | ||
| 34 | buf.Truncate(buf.Len() - 1) | ||
| 35 | |||
| 36 | if len(parts) > 1 { | ||
| 37 | buf.Write([]byte{'.'}) | ||
| 38 | buf.WriteString(parts[1]) | ||
| 39 | } | ||
| 40 | return buf.String() | ||
| 41 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go new file mode 100644 index 0000000..bce923f --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ftoa.go | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "strconv" | ||
| 5 | "strings" | ||
| 6 | ) | ||
| 7 | |||
| 8 | func stripTrailingZeros(s string) string { | ||
| 9 | if !strings.ContainsRune(s, '.') { | ||
| 10 | return s | ||
| 11 | } | ||
| 12 | offset := len(s) - 1 | ||
| 13 | for offset > 0 { | ||
| 14 | if s[offset] == '.' { | ||
| 15 | offset-- | ||
| 16 | break | ||
| 17 | } | ||
| 18 | if s[offset] != '0' { | ||
| 19 | break | ||
| 20 | } | ||
| 21 | offset-- | ||
| 22 | } | ||
| 23 | return s[:offset+1] | ||
| 24 | } | ||
| 25 | |||
| 26 | func stripTrailingDigits(s string, digits int) string { | ||
| 27 | if i := strings.Index(s, "."); i >= 0 { | ||
| 28 | if digits <= 0 { | ||
| 29 | return s[:i] | ||
| 30 | } | ||
| 31 | i++ | ||
| 32 | if i+digits >= len(s) { | ||
| 33 | return s | ||
| 34 | } | ||
| 35 | return s[:i+digits] | ||
| 36 | } | ||
| 37 | return s | ||
| 38 | } | ||
| 39 | |||
| 40 | // Ftoa converts a float to a string with no trailing zeros. | ||
| 41 | func Ftoa(num float64) string { | ||
| 42 | return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) | ||
| 43 | } | ||
| 44 | |||
| 45 | // FtoaWithDigits converts a float to a string but limits the resulting string | ||
| 46 | // to the given number of decimal places, and no trailing zeros. | ||
| 47 | func FtoaWithDigits(num float64, digits int) string { | ||
| 48 | return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) | ||
| 49 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/humanize.go b/vendor/github.com/dustin/go-humanize/humanize.go new file mode 100644 index 0000000..a2c2da3 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/humanize.go | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | /* | ||
| 2 | Package humanize converts boring ugly numbers to human-friendly strings and back. | ||
| 3 | |||
| 4 | Durations can be turned into strings such as "3 days ago", numbers | ||
| 5 | representing sizes like 82854982 into useful strings like, "83 MB" or | ||
| 6 | "79 MiB" (whichever you prefer). | ||
| 7 | */ | ||
| 8 | package humanize | ||
| diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go new file mode 100644 index 0000000..6470d0d --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/number.go | |||
| @@ -0,0 +1,192 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | /* | ||
| 4 | Slightly adapted from the source to fit go-humanize. | ||
| 5 | |||
| 6 | Author: https://github.com/gorhill | ||
| 7 | Source: https://gist.github.com/gorhill/5285193 | ||
| 8 | |||
| 9 | */ | ||
| 10 | |||
| 11 | import ( | ||
| 12 | "math" | ||
| 13 | "strconv" | ||
| 14 | ) | ||
| 15 | |||
| 16 | var ( | ||
| 17 | renderFloatPrecisionMultipliers = [...]float64{ | ||
| 18 | 1, | ||
| 19 | 10, | ||
| 20 | 100, | ||
| 21 | 1000, | ||
| 22 | 10000, | ||
| 23 | 100000, | ||
| 24 | 1000000, | ||
| 25 | 10000000, | ||
| 26 | 100000000, | ||
| 27 | 1000000000, | ||
| 28 | } | ||
| 29 | |||
| 30 | renderFloatPrecisionRounders = [...]float64{ | ||
| 31 | 0.5, | ||
| 32 | 0.05, | ||
| 33 | 0.005, | ||
| 34 | 0.0005, | ||
| 35 | 0.00005, | ||
| 36 | 0.000005, | ||
| 37 | 0.0000005, | ||
| 38 | 0.00000005, | ||
| 39 | 0.000000005, | ||
| 40 | 0.0000000005, | ||
| 41 | } | ||
| 42 | ) | ||
| 43 | |||
| 44 | // FormatFloat produces a formatted number as string based on the following user-specified criteria: | ||
| 45 | // * thousands separator | ||
| 46 | // * decimal separator | ||
| 47 | // * decimal precision | ||
| 48 | // | ||
| 49 | // Usage: s := RenderFloat(format, n) | ||
| 50 | // The format parameter tells how to render the number n. | ||
| 51 | // | ||
| 52 | // See examples: http://play.golang.org/p/LXc1Ddm1lJ | ||
| 53 | // | ||
| 54 | // Examples of format strings, given n = 12345.6789: | ||
| 55 | // "#,###.##" => "12,345.67" | ||
| 56 | // "#,###." => "12,345" | ||
| 57 | // "#,###" => "12345,678" | ||
| 58 | // "#\u202F###,##" => "12โฏ345,68" | ||
| 59 | // "#.###,###### => 12.345,678900 | ||
| 60 | // "" (aka default format) => 12,345.67 | ||
| 61 | // | ||
| 62 | // The highest precision allowed is 9 digits after the decimal symbol. | ||
| 63 | // There is also a version for integer number, FormatInteger(), | ||
| 64 | // which is convenient for calls within template. | ||
| 65 | func FormatFloat(format string, n float64) string { | ||
| 66 | // Special cases: | ||
| 67 | // NaN = "NaN" | ||
| 68 | // +Inf = "+Infinity" | ||
| 69 | // -Inf = "-Infinity" | ||
| 70 | if math.IsNaN(n) { | ||
| 71 | return "NaN" | ||
| 72 | } | ||
| 73 | if n > math.MaxFloat64 { | ||
| 74 | return "Infinity" | ||
| 75 | } | ||
| 76 | if n < (0.0 - math.MaxFloat64) { | ||
| 77 | return "-Infinity" | ||
| 78 | } | ||
| 79 | |||
| 80 | // default format | ||
| 81 | precision := 2 | ||
| 82 | decimalStr := "." | ||
| 83 | thousandStr := "," | ||
| 84 | positiveStr := "" | ||
| 85 | negativeStr := "-" | ||
| 86 | |||
| 87 | if len(format) > 0 { | ||
| 88 | format := []rune(format) | ||
| 89 | |||
| 90 | // If there is an explicit format directive, | ||
| 91 | // then default values are these: | ||
| 92 | precision = 9 | ||
| 93 | thousandStr = "" | ||
| 94 | |||
| 95 | // collect indices of meaningful formatting directives | ||
| 96 | formatIndx := []int{} | ||
| 97 | for i, char := range format { | ||
| 98 | if char != '#' && char != '0' { | ||
| 99 | formatIndx = append(formatIndx, i) | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | if len(formatIndx) > 0 { | ||
| 104 | // Directive at index 0: | ||
| 105 | // Must be a '+' | ||
| 106 | // Raise an error if not the case | ||
| 107 | // index: 0123456789 | ||
| 108 | // +0.000,000 | ||
| 109 | // +000,000.0 | ||
| 110 | // +0000.00 | ||
| 111 | // +0000 | ||
| 112 | if formatIndx[0] == 0 { | ||
| 113 | if format[formatIndx[0]] != '+' { | ||
| 114 | panic("RenderFloat(): invalid positive sign directive") | ||
| 115 | } | ||
| 116 | positiveStr = "+" | ||
| 117 | formatIndx = formatIndx[1:] | ||
| 118 | } | ||
| 119 | |||
| 120 | // Two directives: | ||
| 121 | // First is thousands separator | ||
| 122 | // Raise an error if not followed by 3-digit | ||
| 123 | // 0123456789 | ||
| 124 | // 0.000,000 | ||
| 125 | // 000,000.00 | ||
| 126 | if len(formatIndx) == 2 { | ||
| 127 | if (formatIndx[1] - formatIndx[0]) != 4 { | ||
| 128 | panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") | ||
| 129 | } | ||
| 130 | thousandStr = string(format[formatIndx[0]]) | ||
| 131 | formatIndx = formatIndx[1:] | ||
| 132 | } | ||
| 133 | |||
| 134 | // One directive: | ||
| 135 | // Directive is decimal separator | ||
| 136 | // The number of digit-specifier following the separator indicates wanted precision | ||
| 137 | // 0123456789 | ||
| 138 | // 0.00 | ||
| 139 | // 000,0000 | ||
| 140 | if len(formatIndx) == 1 { | ||
| 141 | decimalStr = string(format[formatIndx[0]]) | ||
| 142 | precision = len(format) - formatIndx[0] - 1 | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | // generate sign part | ||
| 148 | var signStr string | ||
| 149 | if n >= 0.000000001 { | ||
| 150 | signStr = positiveStr | ||
| 151 | } else if n <= -0.000000001 { | ||
| 152 | signStr = negativeStr | ||
| 153 | n = -n | ||
| 154 | } else { | ||
| 155 | signStr = "" | ||
| 156 | n = 0.0 | ||
| 157 | } | ||
| 158 | |||
| 159 | // split number into integer and fractional parts | ||
| 160 | intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) | ||
| 161 | |||
| 162 | // generate integer part string | ||
| 163 | intStr := strconv.FormatInt(int64(intf), 10) | ||
| 164 | |||
| 165 | // add thousand separator if required | ||
| 166 | if len(thousandStr) > 0 { | ||
| 167 | for i := len(intStr); i > 3; { | ||
| 168 | i -= 3 | ||
| 169 | intStr = intStr[:i] + thousandStr + intStr[i:] | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | // no fractional part, we can leave now | ||
| 174 | if precision == 0 { | ||
| 175 | return signStr + intStr | ||
| 176 | } | ||
| 177 | |||
| 178 | // generate fractional part | ||
| 179 | fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) | ||
| 180 | // may need padding | ||
| 181 | if len(fracStr) < precision { | ||
| 182 | fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr | ||
| 183 | } | ||
| 184 | |||
| 185 | return signStr + intStr + decimalStr + fracStr | ||
| 186 | } | ||
| 187 | |||
| 188 | // FormatInteger produces a formatted number as string. | ||
| 189 | // See FormatFloat. | ||
| 190 | func FormatInteger(format string, n int) string { | ||
| 191 | return FormatFloat(format, float64(n)) | ||
| 192 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/ordinals.go b/vendor/github.com/dustin/go-humanize/ordinals.go new file mode 100644 index 0000000..43d88a8 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ordinals.go | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import "strconv" | ||
| 4 | |||
| 5 | // Ordinal gives you the input number in a rank/ordinal format. | ||
| 6 | // | ||
| 7 | // Ordinal(3) -> 3rd | ||
| 8 | func Ordinal(x int) string { | ||
| 9 | suffix := "th" | ||
| 10 | switch x % 10 { | ||
| 11 | case 1: | ||
| 12 | if x%100 != 11 { | ||
| 13 | suffix = "st" | ||
| 14 | } | ||
| 15 | case 2: | ||
| 16 | if x%100 != 12 { | ||
| 17 | suffix = "nd" | ||
| 18 | } | ||
| 19 | case 3: | ||
| 20 | if x%100 != 13 { | ||
| 21 | suffix = "rd" | ||
| 22 | } | ||
| 23 | } | ||
| 24 | return strconv.Itoa(x) + suffix | ||
| 25 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go new file mode 100644 index 0000000..8b85019 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/si.go | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "errors" | ||
| 5 | "math" | ||
| 6 | "regexp" | ||
| 7 | "strconv" | ||
| 8 | ) | ||
| 9 | |||
| 10 | var siPrefixTable = map[float64]string{ | ||
| 11 | -30: "q", // quecto | ||
| 12 | -27: "r", // ronto | ||
| 13 | -24: "y", // yocto | ||
| 14 | -21: "z", // zepto | ||
| 15 | -18: "a", // atto | ||
| 16 | -15: "f", // femto | ||
| 17 | -12: "p", // pico | ||
| 18 | -9: "n", // nano | ||
| 19 | -6: "ยต", // micro | ||
| 20 | -3: "m", // milli | ||
| 21 | 0: "", | ||
| 22 | 3: "k", // kilo | ||
| 23 | 6: "M", // mega | ||
| 24 | 9: "G", // giga | ||
| 25 | 12: "T", // tera | ||
| 26 | 15: "P", // peta | ||
| 27 | 18: "E", // exa | ||
| 28 | 21: "Z", // zetta | ||
| 29 | 24: "Y", // yotta | ||
| 30 | 27: "R", // ronna | ||
| 31 | 30: "Q", // quetta | ||
| 32 | } | ||
| 33 | |||
| 34 | var revSIPrefixTable = revfmap(siPrefixTable) | ||
| 35 | |||
| 36 | // revfmap reverses the map and precomputes the power multiplier | ||
| 37 | func revfmap(in map[float64]string) map[string]float64 { | ||
| 38 | rv := map[string]float64{} | ||
| 39 | for k, v := range in { | ||
| 40 | rv[v] = math.Pow(10, k) | ||
| 41 | } | ||
| 42 | return rv | ||
| 43 | } | ||
| 44 | |||
| 45 | var riParseRegex *regexp.Regexp | ||
| 46 | |||
| 47 | func init() { | ||
| 48 | ri := `^([\-0-9.]+)\s?([` | ||
| 49 | for _, v := range siPrefixTable { | ||
| 50 | ri += v | ||
| 51 | } | ||
| 52 | ri += `]?)(.*)` | ||
| 53 | |||
| 54 | riParseRegex = regexp.MustCompile(ri) | ||
| 55 | } | ||
| 56 | |||
| 57 | // ComputeSI finds the most appropriate SI prefix for the given number | ||
| 58 | // and returns the prefix along with the value adjusted to be within | ||
| 59 | // that prefix. | ||
| 60 | // | ||
| 61 | // See also: SI, ParseSI. | ||
| 62 | // | ||
| 63 | // e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") | ||
| 64 | func ComputeSI(input float64) (float64, string) { | ||
| 65 | if input == 0 { | ||
| 66 | return 0, "" | ||
| 67 | } | ||
| 68 | mag := math.Abs(input) | ||
| 69 | exponent := math.Floor(logn(mag, 10)) | ||
| 70 | exponent = math.Floor(exponent/3) * 3 | ||
| 71 | |||
| 72 | value := mag / math.Pow(10, exponent) | ||
| 73 | |||
| 74 | // Handle special case where value is exactly 1000.0 | ||
| 75 | // Should return 1 M instead of 1000 k | ||
| 76 | if value == 1000.0 { | ||
| 77 | exponent += 3 | ||
| 78 | value = mag / math.Pow(10, exponent) | ||
| 79 | } | ||
| 80 | |||
| 81 | value = math.Copysign(value, input) | ||
| 82 | |||
| 83 | prefix := siPrefixTable[exponent] | ||
| 84 | return value, prefix | ||
| 85 | } | ||
| 86 | |||
| 87 | // SI returns a string with default formatting. | ||
| 88 | // | ||
| 89 | // SI uses Ftoa to format float value, removing trailing zeros. | ||
| 90 | // | ||
| 91 | // See also: ComputeSI, ParseSI. | ||
| 92 | // | ||
| 93 | // e.g. SI(1000000, "B") -> 1 MB | ||
| 94 | // e.g. SI(2.2345e-12, "F") -> 2.2345 pF | ||
| 95 | func SI(input float64, unit string) string { | ||
| 96 | value, prefix := ComputeSI(input) | ||
| 97 | return Ftoa(value) + " " + prefix + unit | ||
| 98 | } | ||
| 99 | |||
| 100 | // SIWithDigits works like SI but limits the resulting string to the | ||
| 101 | // given number of decimal places. | ||
| 102 | // | ||
| 103 | // e.g. SIWithDigits(1000000, 0, "B") -> 1 MB | ||
| 104 | // e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF | ||
| 105 | func SIWithDigits(input float64, decimals int, unit string) string { | ||
| 106 | value, prefix := ComputeSI(input) | ||
| 107 | return FtoaWithDigits(value, decimals) + " " + prefix + unit | ||
| 108 | } | ||
| 109 | |||
| 110 | var errInvalid = errors.New("invalid input") | ||
| 111 | |||
| 112 | // ParseSI parses an SI string back into the number and unit. | ||
| 113 | // | ||
| 114 | // See also: SI, ComputeSI. | ||
| 115 | // | ||
| 116 | // e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) | ||
| 117 | func ParseSI(input string) (float64, string, error) { | ||
| 118 | found := riParseRegex.FindStringSubmatch(input) | ||
| 119 | if len(found) != 4 { | ||
| 120 | return 0, "", errInvalid | ||
| 121 | } | ||
| 122 | mag := revSIPrefixTable[found[2]] | ||
| 123 | unit := found[3] | ||
| 124 | |||
| 125 | base, err := strconv.ParseFloat(found[1], 64) | ||
| 126 | return base * mag, unit, err | ||
| 127 | } | ||
| diff --git a/vendor/github.com/dustin/go-humanize/times.go b/vendor/github.com/dustin/go-humanize/times.go new file mode 100644 index 0000000..dd3fbf5 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/times.go | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | package humanize | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "math" | ||
| 6 | "sort" | ||
| 7 | "time" | ||
| 8 | ) | ||
| 9 | |||
| 10 | // Seconds-based time units | ||
| 11 | const ( | ||
| 12 | Day = 24 * time.Hour | ||
| 13 | Week = 7 * Day | ||
| 14 | Month = 30 * Day | ||
| 15 | Year = 12 * Month | ||
| 16 | LongTime = 37 * Year | ||
| 17 | ) | ||
| 18 | |||
| 19 | // Time formats a time into a relative string. | ||
| 20 | // | ||
| 21 | // Time(someT) -> "3 weeks ago" | ||
| 22 | func Time(then time.Time) string { | ||
| 23 | return RelTime(then, time.Now(), "ago", "from now") | ||
| 24 | } | ||
| 25 | |||
| 26 | // A RelTimeMagnitude struct contains a relative time point at which | ||
| 27 | // the relative format of time will switch to a new format string. A | ||
| 28 | // slice of these in ascending order by their "D" field is passed to | ||
| 29 | // CustomRelTime to format durations. | ||
| 30 | // | ||
| 31 | // The Format field is a string that may contain a "%s" which will be | ||
| 32 | // replaced with the appropriate signed label (e.g. "ago" or "from | ||
| 33 | // now") and a "%d" that will be replaced by the quantity. | ||
| 34 | // | ||
| 35 | // The DivBy field is the amount of time the time difference must be | ||
| 36 | // divided by in order to display correctly. | ||
| 37 | // | ||
| 38 | // e.g. if D is 2*time.Minute and you want to display "%d minutes %s" | ||
| 39 | // DivBy should be time.Minute so whatever the duration is will be | ||
| 40 | // expressed in minutes. | ||
| 41 | type RelTimeMagnitude struct { | ||
| 42 | D time.Duration | ||
| 43 | Format string | ||
| 44 | DivBy time.Duration | ||
| 45 | } | ||
| 46 | |||
| 47 | var defaultMagnitudes = []RelTimeMagnitude{ | ||
| 48 | {time.Second, "now", time.Second}, | ||
| 49 | {2 * time.Second, "1 second %s", 1}, | ||
| 50 | {time.Minute, "%d seconds %s", time.Second}, | ||
| 51 | {2 * time.Minute, "1 minute %s", 1}, | ||
| 52 | {time.Hour, "%d minutes %s", time.Minute}, | ||
| 53 | {2 * time.Hour, "1 hour %s", 1}, | ||
| 54 | {Day, "%d hours %s", time.Hour}, | ||
| 55 | {2 * Day, "1 day %s", 1}, | ||
| 56 | {Week, "%d days %s", Day}, | ||
| 57 | {2 * Week, "1 week %s", 1}, | ||
| 58 | {Month, "%d weeks %s", Week}, | ||
| 59 | {2 * Month, "1 month %s", 1}, | ||
| 60 | {Year, "%d months %s", Month}, | ||
| 61 | {18 * Month, "1 year %s", 1}, | ||
| 62 | {2 * Year, "2 years %s", 1}, | ||
| 63 | {LongTime, "%d years %s", Year}, | ||
| 64 | {math.MaxInt64, "a long while %s", 1}, | ||
| 65 | } | ||
| 66 | |||
| 67 | // RelTime formats a time into a relative string. | ||
| 68 | // | ||
| 69 | // It takes two times and two labels. In addition to the generic time | ||
| 70 | // delta string (e.g. 5 minutes), the labels are used applied so that | ||
| 71 | // the label corresponding to the smaller time is applied. | ||
| 72 | // | ||
| 73 | // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" | ||
| 74 | func RelTime(a, b time.Time, albl, blbl string) string { | ||
| 75 | return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) | ||
| 76 | } | ||
| 77 | |||
| 78 | // CustomRelTime formats a time into a relative string. | ||
| 79 | // | ||
| 80 | // It takes two times two labels and a table of relative time formats. | ||
| 81 | // In addition to the generic time delta string (e.g. 5 minutes), the | ||
| 82 | // labels are used applied so that the label corresponding to the | ||
| 83 | // smaller time is applied. | ||
| 84 | func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { | ||
| 85 | lbl := albl | ||
| 86 | diff := b.Sub(a) | ||
| 87 | |||
| 88 | if a.After(b) { | ||
| 89 | lbl = blbl | ||
| 90 | diff = a.Sub(b) | ||
| 91 | } | ||
| 92 | |||
| 93 | n := sort.Search(len(magnitudes), func(i int) bool { | ||
| 94 | return magnitudes[i].D > diff | ||
| 95 | }) | ||
| 96 | |||
| 97 | if n >= len(magnitudes) { | ||
| 98 | n = len(magnitudes) - 1 | ||
| 99 | } | ||
| 100 | mag := magnitudes[n] | ||
| 101 | args := []interface{}{} | ||
| 102 | escaped := false | ||
| 103 | for _, ch := range mag.Format { | ||
| 104 | if escaped { | ||
| 105 | switch ch { | ||
| 106 | case 's': | ||
| 107 | args = append(args, lbl) | ||
| 108 | case 'd': | ||
| 109 | args = append(args, diff/mag.DivBy) | ||
| 110 | } | ||
| 111 | escaped = false | ||
| 112 | } else { | ||
| 113 | escaped = ch == '%' | ||
| 114 | } | ||
| 115 | } | ||
| 116 | return fmt.Sprintf(mag.Format, args...) | ||
| 117 | } | ||