Initial Go project structure, docs, build and CI
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package textutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SI selects decimal units (kB, MB, …). The default is IEC (KiB, MiB, …).
|
||||
// It is set once at start-up, before any goroutines format sizes.
|
||||
var SI bool
|
||||
|
||||
var (
|
||||
iecUnits = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
||||
siUnits = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
|
||||
)
|
||||
|
||||
func scaled(n int64) (float64, string) {
|
||||
base, units := 1024.0, iecUnits
|
||||
if SI {
|
||||
base, units = 1000.0, siUnits
|
||||
}
|
||||
neg := n < 0
|
||||
v := float64(n)
|
||||
if neg {
|
||||
v = -v
|
||||
}
|
||||
i := 0
|
||||
for v >= base && i < len(units)-1 {
|
||||
v /= base
|
||||
i++
|
||||
}
|
||||
if neg {
|
||||
v = -v
|
||||
}
|
||||
return v, units[i]
|
||||
}
|
||||
|
||||
func num(v float64, unit string) string {
|
||||
a := v
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
switch {
|
||||
case unit == "B":
|
||||
return strconv.FormatFloat(v, 'f', 0, 64)
|
||||
case a < 10:
|
||||
return strconv.FormatFloat(v, 'f', 2, 64)
|
||||
case a < 100:
|
||||
return strconv.FormatFloat(v, 'f', 1, 64)
|
||||
default:
|
||||
return strconv.FormatFloat(v, 'f', 0, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// Size formats a byte count, e.g. "86.4 GiB".
|
||||
func Size(n int64) string {
|
||||
v, u := scaled(n)
|
||||
return num(v, u) + " " + u
|
||||
}
|
||||
|
||||
// SizeCompact formats a byte count in at most ~7 cells, e.g. "86GiB".
|
||||
func SizeCompact(n int64) string {
|
||||
v, u := scaled(n)
|
||||
a := v
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
if u != "B" && a < 10 {
|
||||
return strconv.FormatFloat(v, 'f', 1, 64) + u
|
||||
}
|
||||
return strconv.FormatFloat(v, 'f', 0, 64) + u
|
||||
}
|
||||
|
||||
// SignedSize formats a delta with an explicit sign.
|
||||
func SignedSize(n int64) string {
|
||||
if n > 0 {
|
||||
return "+" + Size(n)
|
||||
}
|
||||
if n < 0 {
|
||||
return "-" + Size(-n)
|
||||
}
|
||||
return "±0 B"
|
||||
}
|
||||
|
||||
// Count formats an integer with thousands separators.
|
||||
func Count(n int64) string {
|
||||
s := strconv.FormatInt(n, 10)
|
||||
neg := false
|
||||
if n < 0 {
|
||||
neg, s = true, s[1:]
|
||||
}
|
||||
out := make([]byte, 0, len(s)+len(s)/3)
|
||||
for i := range len(s) {
|
||||
if i > 0 && (len(s)-i)%3 == 0 {
|
||||
out = append(out, ',')
|
||||
}
|
||||
out = append(out, s[i])
|
||||
}
|
||||
if neg {
|
||||
return "-" + string(out)
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// Duration formats an elapsed time as mm:ss or h:mm:ss.
|
||||
func Duration(d time.Duration) string {
|
||||
s := int64(d.Seconds())
|
||||
if s >= 3600 {
|
||||
return fmt.Sprintf("%d:%02d:%02d", s/3600, s/60%60, s%60)
|
||||
}
|
||||
return fmt.Sprintf("%02d:%02d", s/60, s%60)
|
||||
}
|
||||
|
||||
// Percent formats part/whole as a percentage.
|
||||
func Percent(part, whole int64) string {
|
||||
if whole <= 0 {
|
||||
return "–"
|
||||
}
|
||||
p := float64(part) * 100 / float64(whole)
|
||||
if p < 10 {
|
||||
return strconv.FormatFloat(p, 'f', 1, 64) + "%"
|
||||
}
|
||||
return strconv.FormatFloat(p, 'f', 0, 64) + "%"
|
||||
}
|
||||
Reference in New Issue
Block a user