mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Show download count info in release list (#10124)
* Show download count info in release list * Use go-humanize
This commit is contained in:
		
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							@@ -32,6 +32,7 @@ require (
 | 
			
		||||
	github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
 | 
			
		||||
	github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73
 | 
			
		||||
	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
			
		||||
	github.com/dustin/go-humanize v1.0.0
 | 
			
		||||
	github.com/editorconfig/editorconfig-core-go/v2 v2.1.1
 | 
			
		||||
	github.com/emirpasic/gods v1.12.0
 | 
			
		||||
	github.com/etcd-io/bbolt v1.3.3 // indirect
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							@@ -133,6 +133,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
 | 
			
		||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
			
		||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 | 
			
		||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
			
		||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 | 
			
		||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 | 
			
		||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ import (
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
@@ -29,6 +28,7 @@ import (
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
 | 
			
		||||
	"github.com/dustin/go-humanize"
 | 
			
		||||
	"github.com/unknwon/com"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -214,40 +214,15 @@ func AvatarLink(email string) string {
 | 
			
		||||
	return SizedAvatarLink(email, DefaultAvatarSize)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Storage space size types
 | 
			
		||||
const (
 | 
			
		||||
	Byte  = 1
 | 
			
		||||
	KByte = Byte * 1024
 | 
			
		||||
	MByte = KByte * 1024
 | 
			
		||||
	GByte = MByte * 1024
 | 
			
		||||
	TByte = GByte * 1024
 | 
			
		||||
	PByte = TByte * 1024
 | 
			
		||||
	EByte = PByte * 1024
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func logn(n, b float64) float64 {
 | 
			
		||||
	return math.Log(n) / math.Log(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func humanateBytes(s uint64, base float64, sizes []string) string {
 | 
			
		||||
	if s < 10 {
 | 
			
		||||
		return fmt.Sprintf("%dB", s)
 | 
			
		||||
	}
 | 
			
		||||
	e := math.Floor(logn(float64(s), base))
 | 
			
		||||
	suffix := sizes[int(e)]
 | 
			
		||||
	val := float64(s) / math.Pow(base, math.Floor(e))
 | 
			
		||||
	f := "%.0f"
 | 
			
		||||
	if val < 10 {
 | 
			
		||||
		f = "%.1f"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf(f+"%s", val, suffix)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FileSize calculates the file size and generate user-friendly string.
 | 
			
		||||
func FileSize(s int64) string {
 | 
			
		||||
	sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
 | 
			
		||||
	return humanateBytes(uint64(s), 1024, sizes)
 | 
			
		||||
	return humanize.IBytes(uint64(s))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrettyNumber produces a string form of the given number in base 10 with
 | 
			
		||||
// commas after every three orders of magnitud
 | 
			
		||||
func PrettyNumber(v int64) string {
 | 
			
		||||
	return humanize.Comma(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Subtract deals with subtraction of all types of number.
 | 
			
		||||
 
 | 
			
		||||
@@ -103,19 +103,19 @@ func TestAvatarLink(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestFileSize(t *testing.T) {
 | 
			
		||||
	var size int64 = 512
 | 
			
		||||
	assert.Equal(t, "512B", FileSize(size))
 | 
			
		||||
	assert.Equal(t, "512 B", FileSize(size))
 | 
			
		||||
	size *= 1024
 | 
			
		||||
	assert.Equal(t, "512KB", FileSize(size))
 | 
			
		||||
	assert.Equal(t, "512 KiB", FileSize(size))
 | 
			
		||||
	size *= 1024
 | 
			
		||||
	assert.Equal(t, "512MB", FileSize(size))
 | 
			
		||||
	assert.Equal(t, "512 MiB", FileSize(size))
 | 
			
		||||
	size *= 1024
 | 
			
		||||
	assert.Equal(t, "512GB", FileSize(size))
 | 
			
		||||
	assert.Equal(t, "512 GiB", FileSize(size))
 | 
			
		||||
	size *= 1024
 | 
			
		||||
	assert.Equal(t, "512TB", FileSize(size))
 | 
			
		||||
	assert.Equal(t, "512 TiB", FileSize(size))
 | 
			
		||||
	size *= 1024
 | 
			
		||||
	assert.Equal(t, "512PB", FileSize(size))
 | 
			
		||||
	assert.Equal(t, "512 PiB", FileSize(size))
 | 
			
		||||
	size *= 4
 | 
			
		||||
	assert.Equal(t, "2.0EB", FileSize(size))
 | 
			
		||||
	assert.Equal(t, "2.0 EiB", FileSize(size))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSubtract(t *testing.T) {
 | 
			
		||||
 
 | 
			
		||||
@@ -93,6 +93,7 @@ func NewFuncMap() []template.FuncMap {
 | 
			
		||||
		"TimeSinceUnix": timeutil.TimeSinceUnix,
 | 
			
		||||
		"RawTimeSince":  timeutil.RawTimeSince,
 | 
			
		||||
		"FileSize":      base.FileSize,
 | 
			
		||||
		"PrettyNumber":  base.PrettyNumber,
 | 
			
		||||
		"Subtract":      base.Subtract,
 | 
			
		||||
		"EntryIcon":     base.EntryIcon,
 | 
			
		||||
		"MigrationIcon": MigrationIcon,
 | 
			
		||||
 
 | 
			
		||||
@@ -1565,6 +1565,7 @@ release.deletion_success = The release has been deleted.
 | 
			
		||||
release.tag_name_already_exist = A release with this tag name already exists.
 | 
			
		||||
release.tag_name_invalid = The tag name is not valid.
 | 
			
		||||
release.downloads = Downloads
 | 
			
		||||
release.download_count = Downloads: %s
 | 
			
		||||
 | 
			
		||||
branch.name = Branch Name
 | 
			
		||||
branch.search = Search branches
 | 
			
		||||
 
 | 
			
		||||
@@ -84,6 +84,7 @@
 | 
			
		||||
									{{if .Attachments}}
 | 
			
		||||
										{{range .Attachments}}
 | 
			
		||||
										<li>
 | 
			
		||||
											<span class="ui text right" data-tooltip="{{$.i18n.Tr "repo.release.download_count" (.DownloadCount | PrettyNumber)}}" data-position="bottom right"><i class="ui octicon octicon-info"></i></span>
 | 
			
		||||
											<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}">
 | 
			
		||||
												<strong><span class="ui image octicon octicon-package" title='{{.Name}}'></span> {{.Name}}</strong>
 | 
			
		||||
												<span class="ui text grey right">{{.Size | FileSize}}</span>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/dustin/go-humanize/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/dustin/go-humanize/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
sudo: false
 | 
			
		||||
language: go
 | 
			
		||||
go:
 | 
			
		||||
  - 1.3.x
 | 
			
		||||
  - 1.5.x
 | 
			
		||||
  - 1.6.x
 | 
			
		||||
  - 1.7.x
 | 
			
		||||
  - 1.8.x
 | 
			
		||||
  - 1.9.x
 | 
			
		||||
  - master
 | 
			
		||||
matrix:
 | 
			
		||||
  allow_failures:
 | 
			
		||||
    - go: master
 | 
			
		||||
  fast_finish: true
 | 
			
		||||
install:
 | 
			
		||||
  - # 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).
 | 
			
		||||
script:
 | 
			
		||||
  - go get -t -v ./...
 | 
			
		||||
  - diff -u <(echo -n) <(gofmt -d -s .)
 | 
			
		||||
  - go tool vet .
 | 
			
		||||
  - go test -v -race ./...
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/dustin/go-humanize/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/dustin/go-humanize/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
Copyright (c) 2005-2008  Dustin Sallings <dustin@spy.net>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
 | 
			
		||||
<http://www.opensource.org/licenses/mit-license.php>
 | 
			
		||||
							
								
								
									
										124
									
								
								vendor/github.com/dustin/go-humanize/README.markdown
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								vendor/github.com/dustin/go-humanize/README.markdown
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
# Humane Units [](https://travis-ci.org/dustin/go-humanize) [](https://godoc.org/github.com/dustin/go-humanize)
 | 
			
		||||
 | 
			
		||||
Just a few functions for helping humanize times and sizes.
 | 
			
		||||
 | 
			
		||||
`go get` it as `github.com/dustin/go-humanize`, import it as
 | 
			
		||||
`"github.com/dustin/go-humanize"`, use it as `humanize`.
 | 
			
		||||
 | 
			
		||||
See [godoc](https://godoc.org/github.com/dustin/go-humanize) for
 | 
			
		||||
complete documentation.
 | 
			
		||||
 | 
			
		||||
## Sizes
 | 
			
		||||
 | 
			
		||||
This lets you take numbers like `82854982` and convert them to useful
 | 
			
		||||
strings like, `83 MB` or `79 MiB` (whichever you prefer).
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Times
 | 
			
		||||
 | 
			
		||||
This lets you take a `time.Time` and spit it out in relative terms.
 | 
			
		||||
For example, `12 seconds ago` or `3 days from now`.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Thanks to Kyle Lemons for the time implementation from an IRC
 | 
			
		||||
conversation one day. It's pretty neat.
 | 
			
		||||
 | 
			
		||||
## Ordinals
 | 
			
		||||
 | 
			
		||||
From a [mailing list discussion][odisc] where a user wanted to be able
 | 
			
		||||
to label ordinals.
 | 
			
		||||
 | 
			
		||||
    0 -> 0th
 | 
			
		||||
    1 -> 1st
 | 
			
		||||
    2 -> 2nd
 | 
			
		||||
    3 -> 3rd
 | 
			
		||||
    4 -> 4th
 | 
			
		||||
    [...]
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Commas
 | 
			
		||||
 | 
			
		||||
Want to shove commas into numbers? Be my guest.
 | 
			
		||||
 | 
			
		||||
    0 -> 0
 | 
			
		||||
    100 -> 100
 | 
			
		||||
    1000 -> 1,000
 | 
			
		||||
    1000000000 -> 1,000,000,000
 | 
			
		||||
    -100000 -> -100,000
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Ftoa
 | 
			
		||||
 | 
			
		||||
Nicer float64 formatter that removes trailing zeros.
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
fmt.Printf("%f", 2.24)                // 2.240000
 | 
			
		||||
fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24
 | 
			
		||||
fmt.Printf("%f", 2.0)                 // 2.000000
 | 
			
		||||
fmt.Printf("%s", humanize.Ftoa(2.0))  // 2
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## SI notation
 | 
			
		||||
 | 
			
		||||
Format numbers with [SI notation][sinotation].
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
humanize.SI(0.00000000223, "M") // 2.23 nM
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## English-specific functions
 | 
			
		||||
 | 
			
		||||
The following functions are in the `humanize/english` subpackage.
 | 
			
		||||
 | 
			
		||||
### Plurals
 | 
			
		||||
 | 
			
		||||
Simple English pluralization
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
english.PluralWord(1, "object", "") // object
 | 
			
		||||
english.PluralWord(42, "object", "") // objects
 | 
			
		||||
english.PluralWord(2, "bus", "") // buses
 | 
			
		||||
english.PluralWord(99, "locus", "loci") // loci
 | 
			
		||||
 | 
			
		||||
english.Plural(1, "object", "") // 1 object
 | 
			
		||||
english.Plural(42, "object", "") // 42 objects
 | 
			
		||||
english.Plural(2, "bus", "") // 2 buses
 | 
			
		||||
english.Plural(99, "locus", "loci") // 99 loci
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Word series
 | 
			
		||||
 | 
			
		||||
Format comma-separated words lists with conjuctions:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
english.WordSeries([]string{"foo"}, "and") // foo
 | 
			
		||||
english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar
 | 
			
		||||
english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz
 | 
			
		||||
 | 
			
		||||
english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion
 | 
			
		||||
[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix
 | 
			
		||||
							
								
								
									
										31
									
								
								vendor/github.com/dustin/go-humanize/big.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/dustin/go-humanize/big.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math/big"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// order of magnitude (to a max order)
 | 
			
		||||
func oomm(n, b *big.Int, maxmag int) (float64, int) {
 | 
			
		||||
	mag := 0
 | 
			
		||||
	m := &big.Int{}
 | 
			
		||||
	for n.Cmp(b) >= 0 {
 | 
			
		||||
		n.DivMod(n, b, m)
 | 
			
		||||
		mag++
 | 
			
		||||
		if mag == maxmag && maxmag >= 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// total order of magnitude
 | 
			
		||||
// (same as above, but with no upper limit)
 | 
			
		||||
func oom(n, b *big.Int) (float64, int) {
 | 
			
		||||
	mag := 0
 | 
			
		||||
	m := &big.Int{}
 | 
			
		||||
	for n.Cmp(b) >= 0 {
 | 
			
		||||
		n.DivMod(n, b, m)
 | 
			
		||||
		mag++
 | 
			
		||||
	}
 | 
			
		||||
	return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										173
									
								
								vendor/github.com/dustin/go-humanize/bigbytes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								vendor/github.com/dustin/go-humanize/bigbytes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	bigIECExp = big.NewInt(1024)
 | 
			
		||||
 | 
			
		||||
	// BigByte is one byte in bit.Ints
 | 
			
		||||
	BigByte = big.NewInt(1)
 | 
			
		||||
	// BigKiByte is 1,024 bytes in bit.Ints
 | 
			
		||||
	BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp)
 | 
			
		||||
	// BigMiByte is 1,024 k bytes in bit.Ints
 | 
			
		||||
	BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp)
 | 
			
		||||
	// BigGiByte is 1,024 m bytes in bit.Ints
 | 
			
		||||
	BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp)
 | 
			
		||||
	// BigTiByte is 1,024 g bytes in bit.Ints
 | 
			
		||||
	BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp)
 | 
			
		||||
	// BigPiByte is 1,024 t bytes in bit.Ints
 | 
			
		||||
	BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp)
 | 
			
		||||
	// BigEiByte is 1,024 p bytes in bit.Ints
 | 
			
		||||
	BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp)
 | 
			
		||||
	// BigZiByte is 1,024 e bytes in bit.Ints
 | 
			
		||||
	BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp)
 | 
			
		||||
	// BigYiByte is 1,024 z bytes in bit.Ints
 | 
			
		||||
	BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	bigSIExp = big.NewInt(1000)
 | 
			
		||||
 | 
			
		||||
	// BigSIByte is one SI byte in big.Ints
 | 
			
		||||
	BigSIByte = big.NewInt(1)
 | 
			
		||||
	// BigKByte is 1,000 SI bytes in big.Ints
 | 
			
		||||
	BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp)
 | 
			
		||||
	// BigMByte is 1,000 SI k bytes in big.Ints
 | 
			
		||||
	BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp)
 | 
			
		||||
	// BigGByte is 1,000 SI m bytes in big.Ints
 | 
			
		||||
	BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp)
 | 
			
		||||
	// BigTByte is 1,000 SI g bytes in big.Ints
 | 
			
		||||
	BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp)
 | 
			
		||||
	// BigPByte is 1,000 SI t bytes in big.Ints
 | 
			
		||||
	BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp)
 | 
			
		||||
	// BigEByte is 1,000 SI p bytes in big.Ints
 | 
			
		||||
	BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp)
 | 
			
		||||
	// BigZByte is 1,000 SI e bytes in big.Ints
 | 
			
		||||
	BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp)
 | 
			
		||||
	// BigYByte is 1,000 SI z bytes in big.Ints
 | 
			
		||||
	BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var bigBytesSizeTable = map[string]*big.Int{
 | 
			
		||||
	"b":   BigByte,
 | 
			
		||||
	"kib": BigKiByte,
 | 
			
		||||
	"kb":  BigKByte,
 | 
			
		||||
	"mib": BigMiByte,
 | 
			
		||||
	"mb":  BigMByte,
 | 
			
		||||
	"gib": BigGiByte,
 | 
			
		||||
	"gb":  BigGByte,
 | 
			
		||||
	"tib": BigTiByte,
 | 
			
		||||
	"tb":  BigTByte,
 | 
			
		||||
	"pib": BigPiByte,
 | 
			
		||||
	"pb":  BigPByte,
 | 
			
		||||
	"eib": BigEiByte,
 | 
			
		||||
	"eb":  BigEByte,
 | 
			
		||||
	"zib": BigZiByte,
 | 
			
		||||
	"zb":  BigZByte,
 | 
			
		||||
	"yib": BigYiByte,
 | 
			
		||||
	"yb":  BigYByte,
 | 
			
		||||
	// Without suffix
 | 
			
		||||
	"":   BigByte,
 | 
			
		||||
	"ki": BigKiByte,
 | 
			
		||||
	"k":  BigKByte,
 | 
			
		||||
	"mi": BigMiByte,
 | 
			
		||||
	"m":  BigMByte,
 | 
			
		||||
	"gi": BigGiByte,
 | 
			
		||||
	"g":  BigGByte,
 | 
			
		||||
	"ti": BigTiByte,
 | 
			
		||||
	"t":  BigTByte,
 | 
			
		||||
	"pi": BigPiByte,
 | 
			
		||||
	"p":  BigPByte,
 | 
			
		||||
	"ei": BigEiByte,
 | 
			
		||||
	"e":  BigEByte,
 | 
			
		||||
	"z":  BigZByte,
 | 
			
		||||
	"zi": BigZiByte,
 | 
			
		||||
	"y":  BigYByte,
 | 
			
		||||
	"yi": BigYiByte,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var ten = big.NewInt(10)
 | 
			
		||||
 | 
			
		||||
func humanateBigBytes(s, base *big.Int, sizes []string) string {
 | 
			
		||||
	if s.Cmp(ten) < 0 {
 | 
			
		||||
		return fmt.Sprintf("%d B", s)
 | 
			
		||||
	}
 | 
			
		||||
	c := (&big.Int{}).Set(s)
 | 
			
		||||
	val, mag := oomm(c, base, len(sizes)-1)
 | 
			
		||||
	suffix := sizes[mag]
 | 
			
		||||
	f := "%.0f %s"
 | 
			
		||||
	if val < 10 {
 | 
			
		||||
		f = "%.1f %s"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf(f, val, suffix)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BigBytes produces a human readable representation of an SI size.
 | 
			
		||||
//
 | 
			
		||||
// See also: ParseBigBytes.
 | 
			
		||||
//
 | 
			
		||||
// BigBytes(82854982) -> 83 MB
 | 
			
		||||
func BigBytes(s *big.Int) string {
 | 
			
		||||
	sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
 | 
			
		||||
	return humanateBigBytes(s, bigSIExp, sizes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BigIBytes produces a human readable representation of an IEC size.
 | 
			
		||||
//
 | 
			
		||||
// See also: ParseBigBytes.
 | 
			
		||||
//
 | 
			
		||||
// BigIBytes(82854982) -> 79 MiB
 | 
			
		||||
func BigIBytes(s *big.Int) string {
 | 
			
		||||
	sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
 | 
			
		||||
	return humanateBigBytes(s, bigIECExp, sizes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseBigBytes parses a string representation of bytes into the number
 | 
			
		||||
// of bytes it represents.
 | 
			
		||||
//
 | 
			
		||||
// See also: BigBytes, BigIBytes.
 | 
			
		||||
//
 | 
			
		||||
// ParseBigBytes("42 MB") -> 42000000, nil
 | 
			
		||||
// ParseBigBytes("42 mib") -> 44040192, nil
 | 
			
		||||
func ParseBigBytes(s string) (*big.Int, error) {
 | 
			
		||||
	lastDigit := 0
 | 
			
		||||
	hasComma := false
 | 
			
		||||
	for _, r := range s {
 | 
			
		||||
		if !(unicode.IsDigit(r) || r == '.' || r == ',') {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if r == ',' {
 | 
			
		||||
			hasComma = true
 | 
			
		||||
		}
 | 
			
		||||
		lastDigit++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	num := s[:lastDigit]
 | 
			
		||||
	if hasComma {
 | 
			
		||||
		num = strings.Replace(num, ",", "", -1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val := &big.Rat{}
 | 
			
		||||
	_, err := fmt.Sscanf(num, "%f", val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
 | 
			
		||||
	if m, ok := bigBytesSizeTable[extra]; ok {
 | 
			
		||||
		mv := (&big.Rat{}).SetInt(m)
 | 
			
		||||
		val.Mul(val, mv)
 | 
			
		||||
		rv := &big.Int{}
 | 
			
		||||
		rv.Div(val.Num(), val.Denom())
 | 
			
		||||
		return rv, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, fmt.Errorf("unhandled size name: %v", extra)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										143
									
								
								vendor/github.com/dustin/go-humanize/bytes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/dustin/go-humanize/bytes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IEC Sizes.
 | 
			
		||||
// kibis of bits
 | 
			
		||||
const (
 | 
			
		||||
	Byte = 1 << (iota * 10)
 | 
			
		||||
	KiByte
 | 
			
		||||
	MiByte
 | 
			
		||||
	GiByte
 | 
			
		||||
	TiByte
 | 
			
		||||
	PiByte
 | 
			
		||||
	EiByte
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SI Sizes.
 | 
			
		||||
const (
 | 
			
		||||
	IByte = 1
 | 
			
		||||
	KByte = IByte * 1000
 | 
			
		||||
	MByte = KByte * 1000
 | 
			
		||||
	GByte = MByte * 1000
 | 
			
		||||
	TByte = GByte * 1000
 | 
			
		||||
	PByte = TByte * 1000
 | 
			
		||||
	EByte = PByte * 1000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var bytesSizeTable = map[string]uint64{
 | 
			
		||||
	"b":   Byte,
 | 
			
		||||
	"kib": KiByte,
 | 
			
		||||
	"kb":  KByte,
 | 
			
		||||
	"mib": MiByte,
 | 
			
		||||
	"mb":  MByte,
 | 
			
		||||
	"gib": GiByte,
 | 
			
		||||
	"gb":  GByte,
 | 
			
		||||
	"tib": TiByte,
 | 
			
		||||
	"tb":  TByte,
 | 
			
		||||
	"pib": PiByte,
 | 
			
		||||
	"pb":  PByte,
 | 
			
		||||
	"eib": EiByte,
 | 
			
		||||
	"eb":  EByte,
 | 
			
		||||
	// Without suffix
 | 
			
		||||
	"":   Byte,
 | 
			
		||||
	"ki": KiByte,
 | 
			
		||||
	"k":  KByte,
 | 
			
		||||
	"mi": MiByte,
 | 
			
		||||
	"m":  MByte,
 | 
			
		||||
	"gi": GiByte,
 | 
			
		||||
	"g":  GByte,
 | 
			
		||||
	"ti": TiByte,
 | 
			
		||||
	"t":  TByte,
 | 
			
		||||
	"pi": PiByte,
 | 
			
		||||
	"p":  PByte,
 | 
			
		||||
	"ei": EiByte,
 | 
			
		||||
	"e":  EByte,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func logn(n, b float64) float64 {
 | 
			
		||||
	return math.Log(n) / math.Log(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func humanateBytes(s uint64, base float64, sizes []string) string {
 | 
			
		||||
	if s < 10 {
 | 
			
		||||
		return fmt.Sprintf("%d B", s)
 | 
			
		||||
	}
 | 
			
		||||
	e := math.Floor(logn(float64(s), base))
 | 
			
		||||
	suffix := sizes[int(e)]
 | 
			
		||||
	val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
 | 
			
		||||
	f := "%.0f %s"
 | 
			
		||||
	if val < 10 {
 | 
			
		||||
		f = "%.1f %s"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf(f, val, suffix)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bytes produces a human readable representation of an SI size.
 | 
			
		||||
//
 | 
			
		||||
// See also: ParseBytes.
 | 
			
		||||
//
 | 
			
		||||
// Bytes(82854982) -> 83 MB
 | 
			
		||||
func Bytes(s uint64) string {
 | 
			
		||||
	sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
 | 
			
		||||
	return humanateBytes(s, 1000, sizes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IBytes produces a human readable representation of an IEC size.
 | 
			
		||||
//
 | 
			
		||||
// See also: ParseBytes.
 | 
			
		||||
//
 | 
			
		||||
// IBytes(82854982) -> 79 MiB
 | 
			
		||||
func IBytes(s uint64) string {
 | 
			
		||||
	sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
 | 
			
		||||
	return humanateBytes(s, 1024, sizes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseBytes parses a string representation of bytes into the number
 | 
			
		||||
// of bytes it represents.
 | 
			
		||||
//
 | 
			
		||||
// See Also: Bytes, IBytes.
 | 
			
		||||
//
 | 
			
		||||
// ParseBytes("42 MB") -> 42000000, nil
 | 
			
		||||
// ParseBytes("42 mib") -> 44040192, nil
 | 
			
		||||
func ParseBytes(s string) (uint64, error) {
 | 
			
		||||
	lastDigit := 0
 | 
			
		||||
	hasComma := false
 | 
			
		||||
	for _, r := range s {
 | 
			
		||||
		if !(unicode.IsDigit(r) || r == '.' || r == ',') {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if r == ',' {
 | 
			
		||||
			hasComma = true
 | 
			
		||||
		}
 | 
			
		||||
		lastDigit++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	num := s[:lastDigit]
 | 
			
		||||
	if hasComma {
 | 
			
		||||
		num = strings.Replace(num, ",", "", -1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := strconv.ParseFloat(num, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
 | 
			
		||||
	if m, ok := bytesSizeTable[extra]; ok {
 | 
			
		||||
		f *= float64(m)
 | 
			
		||||
		if f >= math.MaxUint64 {
 | 
			
		||||
			return 0, fmt.Errorf("too large: %v", s)
 | 
			
		||||
		}
 | 
			
		||||
		return uint64(f), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0, fmt.Errorf("unhandled size name: %v", extra)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								vendor/github.com/dustin/go-humanize/comma.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/dustin/go-humanize/comma.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Comma produces a string form of the given number in base 10 with
 | 
			
		||||
// commas after every three orders of magnitude.
 | 
			
		||||
//
 | 
			
		||||
// e.g. Comma(834142) -> 834,142
 | 
			
		||||
func Comma(v int64) string {
 | 
			
		||||
	sign := ""
 | 
			
		||||
 | 
			
		||||
	// Min int64 can't be negated to a usable value, so it has to be special cased.
 | 
			
		||||
	if v == math.MinInt64 {
 | 
			
		||||
		return "-9,223,372,036,854,775,808"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v < 0 {
 | 
			
		||||
		sign = "-"
 | 
			
		||||
		v = 0 - v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts := []string{"", "", "", "", "", "", ""}
 | 
			
		||||
	j := len(parts) - 1
 | 
			
		||||
 | 
			
		||||
	for v > 999 {
 | 
			
		||||
		parts[j] = strconv.FormatInt(v%1000, 10)
 | 
			
		||||
		switch len(parts[j]) {
 | 
			
		||||
		case 2:
 | 
			
		||||
			parts[j] = "0" + parts[j]
 | 
			
		||||
		case 1:
 | 
			
		||||
			parts[j] = "00" + parts[j]
 | 
			
		||||
		}
 | 
			
		||||
		v = v / 1000
 | 
			
		||||
		j--
 | 
			
		||||
	}
 | 
			
		||||
	parts[j] = strconv.Itoa(int(v))
 | 
			
		||||
	return sign + strings.Join(parts[j:], ",")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Commaf produces a string form of the given number in base 10 with
 | 
			
		||||
// commas after every three orders of magnitude.
 | 
			
		||||
//
 | 
			
		||||
// e.g. Commaf(834142.32) -> 834,142.32
 | 
			
		||||
func Commaf(v float64) string {
 | 
			
		||||
	buf := &bytes.Buffer{}
 | 
			
		||||
	if v < 0 {
 | 
			
		||||
		buf.Write([]byte{'-'})
 | 
			
		||||
		v = 0 - v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	comma := []byte{','}
 | 
			
		||||
 | 
			
		||||
	parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".")
 | 
			
		||||
	pos := 0
 | 
			
		||||
	if len(parts[0])%3 != 0 {
 | 
			
		||||
		pos += len(parts[0]) % 3
 | 
			
		||||
		buf.WriteString(parts[0][:pos])
 | 
			
		||||
		buf.Write(comma)
 | 
			
		||||
	}
 | 
			
		||||
	for ; pos < len(parts[0]); pos += 3 {
 | 
			
		||||
		buf.WriteString(parts[0][pos : pos+3])
 | 
			
		||||
		buf.Write(comma)
 | 
			
		||||
	}
 | 
			
		||||
	buf.Truncate(buf.Len() - 1)
 | 
			
		||||
 | 
			
		||||
	if len(parts) > 1 {
 | 
			
		||||
		buf.Write([]byte{'.'})
 | 
			
		||||
		buf.WriteString(parts[1])
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CommafWithDigits works like the Commaf but limits the resulting
 | 
			
		||||
// string to the given number of decimal places.
 | 
			
		||||
//
 | 
			
		||||
// e.g. CommafWithDigits(834142.32, 1) -> 834,142.3
 | 
			
		||||
func CommafWithDigits(f float64, decimals int) string {
 | 
			
		||||
	return stripTrailingDigits(Commaf(f), decimals)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BigComma produces a string form of the given big.Int in base 10
 | 
			
		||||
// with commas after every three orders of magnitude.
 | 
			
		||||
func BigComma(b *big.Int) string {
 | 
			
		||||
	sign := ""
 | 
			
		||||
	if b.Sign() < 0 {
 | 
			
		||||
		sign = "-"
 | 
			
		||||
		b.Abs(b)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	athousand := big.NewInt(1000)
 | 
			
		||||
	c := (&big.Int{}).Set(b)
 | 
			
		||||
	_, m := oom(c, athousand)
 | 
			
		||||
	parts := make([]string, m+1)
 | 
			
		||||
	j := len(parts) - 1
 | 
			
		||||
 | 
			
		||||
	mod := &big.Int{}
 | 
			
		||||
	for b.Cmp(athousand) >= 0 {
 | 
			
		||||
		b.DivMod(b, athousand, mod)
 | 
			
		||||
		parts[j] = strconv.FormatInt(mod.Int64(), 10)
 | 
			
		||||
		switch len(parts[j]) {
 | 
			
		||||
		case 2:
 | 
			
		||||
			parts[j] = "0" + parts[j]
 | 
			
		||||
		case 1:
 | 
			
		||||
			parts[j] = "00" + parts[j]
 | 
			
		||||
		}
 | 
			
		||||
		j--
 | 
			
		||||
	}
 | 
			
		||||
	parts[j] = strconv.Itoa(int(b.Int64()))
 | 
			
		||||
	return sign + strings.Join(parts[j:], ",")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								vendor/github.com/dustin/go-humanize/commaf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/dustin/go-humanize/commaf.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
// +build go1.6
 | 
			
		||||
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// BigCommaf produces a string form of the given big.Float in base 10
 | 
			
		||||
// with commas after every three orders of magnitude.
 | 
			
		||||
func BigCommaf(v *big.Float) string {
 | 
			
		||||
	buf := &bytes.Buffer{}
 | 
			
		||||
	if v.Sign() < 0 {
 | 
			
		||||
		buf.Write([]byte{'-'})
 | 
			
		||||
		v.Abs(v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	comma := []byte{','}
 | 
			
		||||
 | 
			
		||||
	parts := strings.Split(v.Text('f', -1), ".")
 | 
			
		||||
	pos := 0
 | 
			
		||||
	if len(parts[0])%3 != 0 {
 | 
			
		||||
		pos += len(parts[0]) % 3
 | 
			
		||||
		buf.WriteString(parts[0][:pos])
 | 
			
		||||
		buf.Write(comma)
 | 
			
		||||
	}
 | 
			
		||||
	for ; pos < len(parts[0]); pos += 3 {
 | 
			
		||||
		buf.WriteString(parts[0][pos : pos+3])
 | 
			
		||||
		buf.Write(comma)
 | 
			
		||||
	}
 | 
			
		||||
	buf.Truncate(buf.Len() - 1)
 | 
			
		||||
 | 
			
		||||
	if len(parts) > 1 {
 | 
			
		||||
		buf.Write([]byte{'.'})
 | 
			
		||||
		buf.WriteString(parts[1])
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/dustin/go-humanize/ftoa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/dustin/go-humanize/ftoa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func stripTrailingZeros(s string) string {
 | 
			
		||||
	offset := len(s) - 1
 | 
			
		||||
	for offset > 0 {
 | 
			
		||||
		if s[offset] == '.' {
 | 
			
		||||
			offset--
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if s[offset] != '0' {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		offset--
 | 
			
		||||
	}
 | 
			
		||||
	return s[:offset+1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func stripTrailingDigits(s string, digits int) string {
 | 
			
		||||
	if i := strings.Index(s, "."); i >= 0 {
 | 
			
		||||
		if digits <= 0 {
 | 
			
		||||
			return s[:i]
 | 
			
		||||
		}
 | 
			
		||||
		i++
 | 
			
		||||
		if i+digits >= len(s) {
 | 
			
		||||
			return s
 | 
			
		||||
		}
 | 
			
		||||
		return s[:i+digits]
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ftoa converts a float to a string with no trailing zeros.
 | 
			
		||||
func Ftoa(num float64) string {
 | 
			
		||||
	return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FtoaWithDigits converts a float to a string but limits the resulting string
 | 
			
		||||
// to the given number of decimal places, and no trailing zeros.
 | 
			
		||||
func FtoaWithDigits(num float64, digits int) string {
 | 
			
		||||
	return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/dustin/go-humanize/humanize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/dustin/go-humanize/humanize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
/*
 | 
			
		||||
Package humanize converts boring ugly numbers to human-friendly strings and back.
 | 
			
		||||
 | 
			
		||||
Durations can be turned into strings such as "3 days ago", numbers
 | 
			
		||||
representing sizes like 82854982 into useful strings like, "83 MB" or
 | 
			
		||||
"79 MiB" (whichever you prefer).
 | 
			
		||||
*/
 | 
			
		||||
package humanize
 | 
			
		||||
							
								
								
									
										192
									
								
								vendor/github.com/dustin/go-humanize/number.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/github.com/dustin/go-humanize/number.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,192 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Slightly adapted from the source to fit go-humanize.
 | 
			
		||||
 | 
			
		||||
Author: https://github.com/gorhill
 | 
			
		||||
Source: https://gist.github.com/gorhill/5285193
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	renderFloatPrecisionMultipliers = [...]float64{
 | 
			
		||||
		1,
 | 
			
		||||
		10,
 | 
			
		||||
		100,
 | 
			
		||||
		1000,
 | 
			
		||||
		10000,
 | 
			
		||||
		100000,
 | 
			
		||||
		1000000,
 | 
			
		||||
		10000000,
 | 
			
		||||
		100000000,
 | 
			
		||||
		1000000000,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	renderFloatPrecisionRounders = [...]float64{
 | 
			
		||||
		0.5,
 | 
			
		||||
		0.05,
 | 
			
		||||
		0.005,
 | 
			
		||||
		0.0005,
 | 
			
		||||
		0.00005,
 | 
			
		||||
		0.000005,
 | 
			
		||||
		0.0000005,
 | 
			
		||||
		0.00000005,
 | 
			
		||||
		0.000000005,
 | 
			
		||||
		0.0000000005,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FormatFloat produces a formatted number as string based on the following user-specified criteria:
 | 
			
		||||
// * thousands separator
 | 
			
		||||
// * decimal separator
 | 
			
		||||
// * decimal precision
 | 
			
		||||
//
 | 
			
		||||
// Usage: s := RenderFloat(format, n)
 | 
			
		||||
// The format parameter tells how to render the number n.
 | 
			
		||||
//
 | 
			
		||||
// See examples: http://play.golang.org/p/LXc1Ddm1lJ
 | 
			
		||||
//
 | 
			
		||||
// Examples of format strings, given n = 12345.6789:
 | 
			
		||||
// "#,###.##" => "12,345.67"
 | 
			
		||||
// "#,###." => "12,345"
 | 
			
		||||
// "#,###" => "12345,678"
 | 
			
		||||
// "#\u202F###,##" => "12 345,68"
 | 
			
		||||
// "#.###,###### => 12.345,678900
 | 
			
		||||
// "" (aka default format) => 12,345.67
 | 
			
		||||
//
 | 
			
		||||
// The highest precision allowed is 9 digits after the decimal symbol.
 | 
			
		||||
// There is also a version for integer number, FormatInteger(),
 | 
			
		||||
// which is convenient for calls within template.
 | 
			
		||||
func FormatFloat(format string, n float64) string {
 | 
			
		||||
	// Special cases:
 | 
			
		||||
	//   NaN = "NaN"
 | 
			
		||||
	//   +Inf = "+Infinity"
 | 
			
		||||
	//   -Inf = "-Infinity"
 | 
			
		||||
	if math.IsNaN(n) {
 | 
			
		||||
		return "NaN"
 | 
			
		||||
	}
 | 
			
		||||
	if n > math.MaxFloat64 {
 | 
			
		||||
		return "Infinity"
 | 
			
		||||
	}
 | 
			
		||||
	if n < -math.MaxFloat64 {
 | 
			
		||||
		return "-Infinity"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// default format
 | 
			
		||||
	precision := 2
 | 
			
		||||
	decimalStr := "."
 | 
			
		||||
	thousandStr := ","
 | 
			
		||||
	positiveStr := ""
 | 
			
		||||
	negativeStr := "-"
 | 
			
		||||
 | 
			
		||||
	if len(format) > 0 {
 | 
			
		||||
		format := []rune(format)
 | 
			
		||||
 | 
			
		||||
		// If there is an explicit format directive,
 | 
			
		||||
		// then default values are these:
 | 
			
		||||
		precision = 9
 | 
			
		||||
		thousandStr = ""
 | 
			
		||||
 | 
			
		||||
		// collect indices of meaningful formatting directives
 | 
			
		||||
		formatIndx := []int{}
 | 
			
		||||
		for i, char := range format {
 | 
			
		||||
			if char != '#' && char != '0' {
 | 
			
		||||
				formatIndx = append(formatIndx, i)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(formatIndx) > 0 {
 | 
			
		||||
			// Directive at index 0:
 | 
			
		||||
			//   Must be a '+'
 | 
			
		||||
			//   Raise an error if not the case
 | 
			
		||||
			// index: 0123456789
 | 
			
		||||
			//        +0.000,000
 | 
			
		||||
			//        +000,000.0
 | 
			
		||||
			//        +0000.00
 | 
			
		||||
			//        +0000
 | 
			
		||||
			if formatIndx[0] == 0 {
 | 
			
		||||
				if format[formatIndx[0]] != '+' {
 | 
			
		||||
					panic("RenderFloat(): invalid positive sign directive")
 | 
			
		||||
				}
 | 
			
		||||
				positiveStr = "+"
 | 
			
		||||
				formatIndx = formatIndx[1:]
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Two directives:
 | 
			
		||||
			//   First is thousands separator
 | 
			
		||||
			//   Raise an error if not followed by 3-digit
 | 
			
		||||
			// 0123456789
 | 
			
		||||
			// 0.000,000
 | 
			
		||||
			// 000,000.00
 | 
			
		||||
			if len(formatIndx) == 2 {
 | 
			
		||||
				if (formatIndx[1] - formatIndx[0]) != 4 {
 | 
			
		||||
					panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
 | 
			
		||||
				}
 | 
			
		||||
				thousandStr = string(format[formatIndx[0]])
 | 
			
		||||
				formatIndx = formatIndx[1:]
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// One directive:
 | 
			
		||||
			//   Directive is decimal separator
 | 
			
		||||
			//   The number of digit-specifier following the separator indicates wanted precision
 | 
			
		||||
			// 0123456789
 | 
			
		||||
			// 0.00
 | 
			
		||||
			// 000,0000
 | 
			
		||||
			if len(formatIndx) == 1 {
 | 
			
		||||
				decimalStr = string(format[formatIndx[0]])
 | 
			
		||||
				precision = len(format) - formatIndx[0] - 1
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// generate sign part
 | 
			
		||||
	var signStr string
 | 
			
		||||
	if n >= 0.000000001 {
 | 
			
		||||
		signStr = positiveStr
 | 
			
		||||
	} else if n <= -0.000000001 {
 | 
			
		||||
		signStr = negativeStr
 | 
			
		||||
		n = -n
 | 
			
		||||
	} else {
 | 
			
		||||
		signStr = ""
 | 
			
		||||
		n = 0.0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// split number into integer and fractional parts
 | 
			
		||||
	intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision])
 | 
			
		||||
 | 
			
		||||
	// generate integer part string
 | 
			
		||||
	intStr := strconv.FormatInt(int64(intf), 10)
 | 
			
		||||
 | 
			
		||||
	// add thousand separator if required
 | 
			
		||||
	if len(thousandStr) > 0 {
 | 
			
		||||
		for i := len(intStr); i > 3; {
 | 
			
		||||
			i -= 3
 | 
			
		||||
			intStr = intStr[:i] + thousandStr + intStr[i:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// no fractional part, we can leave now
 | 
			
		||||
	if precision == 0 {
 | 
			
		||||
		return signStr + intStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// generate fractional part
 | 
			
		||||
	fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision]))
 | 
			
		||||
	// may need padding
 | 
			
		||||
	if len(fracStr) < precision {
 | 
			
		||||
		fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return signStr + intStr + decimalStr + fracStr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatInteger produces a formatted number as string.
 | 
			
		||||
// See FormatFloat.
 | 
			
		||||
func FormatInteger(format string, n int) string {
 | 
			
		||||
	return FormatFloat(format, float64(n))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/github.com/dustin/go-humanize/ordinals.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/dustin/go-humanize/ordinals.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import "strconv"
 | 
			
		||||
 | 
			
		||||
// Ordinal gives you the input number in a rank/ordinal format.
 | 
			
		||||
//
 | 
			
		||||
// Ordinal(3) -> 3rd
 | 
			
		||||
func Ordinal(x int) string {
 | 
			
		||||
	suffix := "th"
 | 
			
		||||
	switch x % 10 {
 | 
			
		||||
	case 1:
 | 
			
		||||
		if x%100 != 11 {
 | 
			
		||||
			suffix = "st"
 | 
			
		||||
		}
 | 
			
		||||
	case 2:
 | 
			
		||||
		if x%100 != 12 {
 | 
			
		||||
			suffix = "nd"
 | 
			
		||||
		}
 | 
			
		||||
	case 3:
 | 
			
		||||
		if x%100 != 13 {
 | 
			
		||||
			suffix = "rd"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return strconv.Itoa(x) + suffix
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								vendor/github.com/dustin/go-humanize/si.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/dustin/go-humanize/si.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"math"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var siPrefixTable = map[float64]string{
 | 
			
		||||
	-24: "y", // yocto
 | 
			
		||||
	-21: "z", // zepto
 | 
			
		||||
	-18: "a", // atto
 | 
			
		||||
	-15: "f", // femto
 | 
			
		||||
	-12: "p", // pico
 | 
			
		||||
	-9:  "n", // nano
 | 
			
		||||
	-6:  "µ", // micro
 | 
			
		||||
	-3:  "m", // milli
 | 
			
		||||
	0:   "",
 | 
			
		||||
	3:   "k", // kilo
 | 
			
		||||
	6:   "M", // mega
 | 
			
		||||
	9:   "G", // giga
 | 
			
		||||
	12:  "T", // tera
 | 
			
		||||
	15:  "P", // peta
 | 
			
		||||
	18:  "E", // exa
 | 
			
		||||
	21:  "Z", // zetta
 | 
			
		||||
	24:  "Y", // yotta
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var revSIPrefixTable = revfmap(siPrefixTable)
 | 
			
		||||
 | 
			
		||||
// revfmap reverses the map and precomputes the power multiplier
 | 
			
		||||
func revfmap(in map[float64]string) map[string]float64 {
 | 
			
		||||
	rv := map[string]float64{}
 | 
			
		||||
	for k, v := range in {
 | 
			
		||||
		rv[v] = math.Pow(10, k)
 | 
			
		||||
	}
 | 
			
		||||
	return rv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var riParseRegex *regexp.Regexp
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	ri := `^([\-0-9.]+)\s?([`
 | 
			
		||||
	for _, v := range siPrefixTable {
 | 
			
		||||
		ri += v
 | 
			
		||||
	}
 | 
			
		||||
	ri += `]?)(.*)`
 | 
			
		||||
 | 
			
		||||
	riParseRegex = regexp.MustCompile(ri)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ComputeSI finds the most appropriate SI prefix for the given number
 | 
			
		||||
// and returns the prefix along with the value adjusted to be within
 | 
			
		||||
// that prefix.
 | 
			
		||||
//
 | 
			
		||||
// See also: SI, ParseSI.
 | 
			
		||||
//
 | 
			
		||||
// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p")
 | 
			
		||||
func ComputeSI(input float64) (float64, string) {
 | 
			
		||||
	if input == 0 {
 | 
			
		||||
		return 0, ""
 | 
			
		||||
	}
 | 
			
		||||
	mag := math.Abs(input)
 | 
			
		||||
	exponent := math.Floor(logn(mag, 10))
 | 
			
		||||
	exponent = math.Floor(exponent/3) * 3
 | 
			
		||||
 | 
			
		||||
	value := mag / math.Pow(10, exponent)
 | 
			
		||||
 | 
			
		||||
	// Handle special case where value is exactly 1000.0
 | 
			
		||||
	// Should return 1 M instead of 1000 k
 | 
			
		||||
	if value == 1000.0 {
 | 
			
		||||
		exponent += 3
 | 
			
		||||
		value = mag / math.Pow(10, exponent)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	value = math.Copysign(value, input)
 | 
			
		||||
 | 
			
		||||
	prefix := siPrefixTable[exponent]
 | 
			
		||||
	return value, prefix
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SI returns a string with default formatting.
 | 
			
		||||
//
 | 
			
		||||
// SI uses Ftoa to format float value, removing trailing zeros.
 | 
			
		||||
//
 | 
			
		||||
// See also: ComputeSI, ParseSI.
 | 
			
		||||
//
 | 
			
		||||
// e.g. SI(1000000, "B") -> 1 MB
 | 
			
		||||
// e.g. SI(2.2345e-12, "F") -> 2.2345 pF
 | 
			
		||||
func SI(input float64, unit string) string {
 | 
			
		||||
	value, prefix := ComputeSI(input)
 | 
			
		||||
	return Ftoa(value) + " " + prefix + unit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SIWithDigits works like SI but limits the resulting string to the
 | 
			
		||||
// given number of decimal places.
 | 
			
		||||
//
 | 
			
		||||
// e.g. SIWithDigits(1000000, 0, "B") -> 1 MB
 | 
			
		||||
// e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF
 | 
			
		||||
func SIWithDigits(input float64, decimals int, unit string) string {
 | 
			
		||||
	value, prefix := ComputeSI(input)
 | 
			
		||||
	return FtoaWithDigits(value, decimals) + " " + prefix + unit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errInvalid = errors.New("invalid input")
 | 
			
		||||
 | 
			
		||||
// ParseSI parses an SI string back into the number and unit.
 | 
			
		||||
//
 | 
			
		||||
// See also: SI, ComputeSI.
 | 
			
		||||
//
 | 
			
		||||
// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil)
 | 
			
		||||
func ParseSI(input string) (float64, string, error) {
 | 
			
		||||
	found := riParseRegex.FindStringSubmatch(input)
 | 
			
		||||
	if len(found) != 4 {
 | 
			
		||||
		return 0, "", errInvalid
 | 
			
		||||
	}
 | 
			
		||||
	mag := revSIPrefixTable[found[2]]
 | 
			
		||||
	unit := found[3]
 | 
			
		||||
 | 
			
		||||
	base, err := strconv.ParseFloat(found[1], 64)
 | 
			
		||||
	return base * mag, unit, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										117
									
								
								vendor/github.com/dustin/go-humanize/times.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/dustin/go-humanize/times.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
package humanize
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Seconds-based time units
 | 
			
		||||
const (
 | 
			
		||||
	Day      = 24 * time.Hour
 | 
			
		||||
	Week     = 7 * Day
 | 
			
		||||
	Month    = 30 * Day
 | 
			
		||||
	Year     = 12 * Month
 | 
			
		||||
	LongTime = 37 * Year
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Time formats a time into a relative string.
 | 
			
		||||
//
 | 
			
		||||
// Time(someT) -> "3 weeks ago"
 | 
			
		||||
func Time(then time.Time) string {
 | 
			
		||||
	return RelTime(then, time.Now(), "ago", "from now")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A RelTimeMagnitude struct contains a relative time point at which
 | 
			
		||||
// the relative format of time will switch to a new format string.  A
 | 
			
		||||
// slice of these in ascending order by their "D" field is passed to
 | 
			
		||||
// CustomRelTime to format durations.
 | 
			
		||||
//
 | 
			
		||||
// The Format field is a string that may contain a "%s" which will be
 | 
			
		||||
// replaced with the appropriate signed label (e.g. "ago" or "from
 | 
			
		||||
// now") and a "%d" that will be replaced by the quantity.
 | 
			
		||||
//
 | 
			
		||||
// The DivBy field is the amount of time the time difference must be
 | 
			
		||||
// divided by in order to display correctly.
 | 
			
		||||
//
 | 
			
		||||
// e.g. if D is 2*time.Minute and you want to display "%d minutes %s"
 | 
			
		||||
// DivBy should be time.Minute so whatever the duration is will be
 | 
			
		||||
// expressed in minutes.
 | 
			
		||||
type RelTimeMagnitude struct {
 | 
			
		||||
	D      time.Duration
 | 
			
		||||
	Format string
 | 
			
		||||
	DivBy  time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var defaultMagnitudes = []RelTimeMagnitude{
 | 
			
		||||
	{time.Second, "now", time.Second},
 | 
			
		||||
	{2 * time.Second, "1 second %s", 1},
 | 
			
		||||
	{time.Minute, "%d seconds %s", time.Second},
 | 
			
		||||
	{2 * time.Minute, "1 minute %s", 1},
 | 
			
		||||
	{time.Hour, "%d minutes %s", time.Minute},
 | 
			
		||||
	{2 * time.Hour, "1 hour %s", 1},
 | 
			
		||||
	{Day, "%d hours %s", time.Hour},
 | 
			
		||||
	{2 * Day, "1 day %s", 1},
 | 
			
		||||
	{Week, "%d days %s", Day},
 | 
			
		||||
	{2 * Week, "1 week %s", 1},
 | 
			
		||||
	{Month, "%d weeks %s", Week},
 | 
			
		||||
	{2 * Month, "1 month %s", 1},
 | 
			
		||||
	{Year, "%d months %s", Month},
 | 
			
		||||
	{18 * Month, "1 year %s", 1},
 | 
			
		||||
	{2 * Year, "2 years %s", 1},
 | 
			
		||||
	{LongTime, "%d years %s", Year},
 | 
			
		||||
	{math.MaxInt64, "a long while %s", 1},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RelTime formats a time into a relative string.
 | 
			
		||||
//
 | 
			
		||||
// It takes two times and two labels.  In addition to the generic time
 | 
			
		||||
// delta string (e.g. 5 minutes), the labels are used applied so that
 | 
			
		||||
// the label corresponding to the smaller time is applied.
 | 
			
		||||
//
 | 
			
		||||
// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
 | 
			
		||||
func RelTime(a, b time.Time, albl, blbl string) string {
 | 
			
		||||
	return CustomRelTime(a, b, albl, blbl, defaultMagnitudes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CustomRelTime formats a time into a relative string.
 | 
			
		||||
//
 | 
			
		||||
// It takes two times two labels and a table of relative time formats.
 | 
			
		||||
// In addition to the generic time delta string (e.g. 5 minutes), the
 | 
			
		||||
// labels are used applied so that the label corresponding to the
 | 
			
		||||
// smaller time is applied.
 | 
			
		||||
func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {
 | 
			
		||||
	lbl := albl
 | 
			
		||||
	diff := b.Sub(a)
 | 
			
		||||
 | 
			
		||||
	if a.After(b) {
 | 
			
		||||
		lbl = blbl
 | 
			
		||||
		diff = a.Sub(b)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n := sort.Search(len(magnitudes), func(i int) bool {
 | 
			
		||||
		return magnitudes[i].D > diff
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if n >= len(magnitudes) {
 | 
			
		||||
		n = len(magnitudes) - 1
 | 
			
		||||
	}
 | 
			
		||||
	mag := magnitudes[n]
 | 
			
		||||
	args := []interface{}{}
 | 
			
		||||
	escaped := false
 | 
			
		||||
	for _, ch := range mag.Format {
 | 
			
		||||
		if escaped {
 | 
			
		||||
			switch ch {
 | 
			
		||||
			case 's':
 | 
			
		||||
				args = append(args, lbl)
 | 
			
		||||
			case 'd':
 | 
			
		||||
				args = append(args, diff/mag.DivBy)
 | 
			
		||||
			}
 | 
			
		||||
			escaped = false
 | 
			
		||||
		} else {
 | 
			
		||||
			escaped = ch == '%'
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf(mag.Format, args...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@@ -121,6 +121,8 @@ github.com/denisenkom/go-mssqldb/internal/decimal
 | 
			
		||||
github.com/denisenkom/go-mssqldb/internal/querytext
 | 
			
		||||
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
 | 
			
		||||
github.com/dgrijalva/jwt-go
 | 
			
		||||
# github.com/dustin/go-humanize v1.0.0
 | 
			
		||||
github.com/dustin/go-humanize
 | 
			
		||||
# github.com/editorconfig/editorconfig-core-go/v2 v2.1.1
 | 
			
		||||
github.com/editorconfig/editorconfig-core-go/v2
 | 
			
		||||
# github.com/edsrzf/mmap-go v1.0.0
 | 
			
		||||
 
 | 
			
		||||
@@ -1631,6 +1631,10 @@
 | 
			
		||||
                                padding-top: 8px;
 | 
			
		||||
                                padding-bottom: 8px;
 | 
			
		||||
                                border-bottom: 1px solid #eeeeee;
 | 
			
		||||
 | 
			
		||||
                                a > .text.right {
 | 
			
		||||
                                    margin-right: 5px;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user