github.com/tawesoft/golib/v2@v2.10.0/legacy/humanize/humanizer.go (about) 1 package humanize 2 3 import ( 4 "math" 5 "time" 6 7 lxstrconv "github.com/tawesoft/golib/v2/legacy/localize" 8 "golang.org/x/text/language" 9 "golang.org/x/text/message" 10 ) 11 12 // String holds an Utf8-encoded and an Ascii-compatible encoding of a string. 13 type String struct { 14 // Utf8 is the native Utf8-encoded Unicode representation 15 Utf8 string 16 17 // Ascii is an alternative version accepted for non-Unicode inputs (such 18 // as when a user does not know how to enter µ on their keyboard (on mine, 19 // it's Right-Alt+m)) or for non-Unicode output (such as legacy systems). 20 Ascii string 21 } 22 23 // Unit describes some quantity e.g. "m" for length in metres, "k" for the SI 24 // unit prefix kilo, "km" for a kilometre. 25 type Unit String 26 27 // Cat concatenates two units (u + v) and returns the result. 28 func (u Unit) Cat(v Unit) Unit { 29 return Unit{ 30 u.Utf8 + v.Utf8, 31 u.Ascii + v.Ascii, 32 } 33 } 34 35 // Part describes some component of a formatting result e.g. 1.5 km or 1 hour 36 type Part struct { 37 Magnitude float64 38 Unit Unit 39 } 40 41 func partEqual(a Part, b Part, epsilon float64) bool { 42 if a.Unit != b.Unit { return false } 43 return math.Abs(a.Magnitude - b.Magnitude) < epsilon 44 } 45 46 func partsEqual(a []Part, b []Part, epsilon float64) bool { 47 if len(a) != len(b) { return false } 48 49 for i := 0; i < len(a); i++ { 50 if !partEqual(a[i], b[i], epsilon) { return false } 51 } 52 53 return true 54 } 55 56 // Humanizer implements a locale-aware way to parse and format humanized 57 // quantities. 58 type Humanizer interface { 59 // Format is a general purpose locale-aware way to format any quantity 60 // with a defined set of factors. The unit argument is the base unit 61 // e.g. s for seconds, m for meters, B for bytes. 62 Format(value float64, unit Unit, factors Factors) String 63 64 FormatNumber(number float64) String // e.g. 12 k 65 FormatDistance(meters float64) String // e.g. 10 µm, 10 km 66 FormatDuration(duration time.Duration) string // e.g. 1 h 50 min 67 FormatSeconds(seconds float64) string // e.g. 1 h 50 min 68 FormatBytesJEDEC(bytes int64) string // e.g. 12 KB, 5 MB 69 FormatBytesIEC(bytes int64) string // e.g. 12 kB, 5 MB 70 FormatBytesSI(bytes int64) string // e.g. 12 KiB, 5 MiB 71 72 // Accept is a general purpose locale-aware way to parse any quantity 73 // with a defined set of factors from the start of the string str. The 74 // provided unit is optional and is accepted if it appears in str. 75 // 76 // Accept returns the value, the number of bytes successfully parsed (which 77 // may be zero), or an error. 78 Accept(str string, unit Unit, factors Factors) (float64, int, error) 79 80 // Parse is a general purpose locale-aware way to parse any quantity 81 // with a defined set of factors. The provided unit is optional and is 82 // accepted if it appears in str. 83 Parse(str string, unit Unit, factors Factors) (float64, error) 84 85 ParseDuration(str string) (time.Duration, error) 86 ParseBytesJEDEC(str string) (int64, error) 87 ParseBytesIEC(str string) (int64, error) 88 ParseBytesSI(str string) (int64, error) 89 } 90 91 type humanizer struct { 92 Tag language.Tag 93 NF lxstrconv.NumberFormat 94 Printer *message.Printer 95 } 96 97 func (h *humanizer) FormatDistance(meters float64) String { 98 return h.Format(meters, CommonUnits.Meter, CommonFactors.Distance) 99 } 100 101 func (h *humanizer) FormatBytesJEDEC(bytes int64) string { 102 return h.Format(float64(bytes), CommonUnits.Byte, CommonFactors.JEDEC).Utf8 103 } 104 105 func (h *humanizer) FormatBytesIEC(bytes int64) string { 106 return h.Format(float64(bytes), CommonUnits.Byte, CommonFactors.IEC).Utf8 107 } 108 109 func (h *humanizer) FormatBytesSI(bytes int64) string { 110 return h.Format(float64(bytes), CommonUnits.Byte, CommonFactors.SI).Utf8 111 } 112 113 func (h *humanizer) FormatDuration(duration time.Duration) string { 114 return h.Format(duration.Seconds(), CommonUnits.Second, CommonFactors.Time).Utf8 115 } 116 117 func (h *humanizer) FormatSeconds(seconds float64) string { 118 return h.Format(seconds, CommonUnits.Second, CommonFactors.Time).Utf8 119 } 120 121 func (h *humanizer) FormatNumber(number float64) String { 122 return h.Format(number, CommonUnits.None, CommonFactors.SI) 123 } 124 125 func (h *humanizer) ParseDuration(str string) (time.Duration, error) { 126 v, err := h.Parse(str, CommonUnits.Second, CommonFactors.Time) 127 return time.Second * time.Duration(v), err 128 } 129 130 func (h *humanizer) ParseBytesJEDEC(str string) (int64, error) { 131 v, err := h.Parse(str, CommonUnits.Byte, CommonFactors.JEDEC) 132 return int64(v), err 133 } 134 135 func (h *humanizer) ParseBytesIEC(str string) (int64, error) { 136 v, err := h.Parse(str, CommonUnits.Byte, CommonFactors.IEC) 137 return int64(v), err 138 } 139 140 func (h *humanizer) ParseBytesSI(str string) (int64, error) { 141 v, err := h.Parse(str, CommonUnits.Byte, CommonFactors.SI) 142 return int64(v), err 143 } 144 145 // NewHumanizer initialises a human language number encoder/decoder for the 146 // given tag (representing a specific language or locale). 147 // 148 // The language.Tag is usually a named language from golang.org/x/text/language 149 // e.g. language.English and controls how numbers are written e.g. comma 150 // placement, decimal point, digits. 151 func NewHumanizer(tag language.Tag, options ... interface{}) Humanizer { 152 return &humanizer{ 153 Tag: tag, 154 NF: lxstrconv.NewDecimalFormat(tag), 155 Printer: message.NewPrinter(tag), 156 } 157 }