mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-30 13:48:54 +08:00
Add round and suffix based value formatter for the Y-axis label representation
Signed-off-by: Xabier Larrakoetxea <slok69@gmail.com>
This commit is contained in:
parent
38f40428a1
commit
a5c7363f43
@ -20,6 +20,7 @@ package linechart
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,33 +43,34 @@ func durationSingleUnitPrettyFormat(d time.Duration, decimals int) string {
|
|||||||
return fmt.Sprintf(dFmt, d.Nanoseconds())
|
return fmt.Sprintf(dFmt, d.Nanoseconds())
|
||||||
// Microseconds.
|
// Microseconds.
|
||||||
case d.Seconds()*1000*1000 < 1000:
|
case d.Seconds()*1000*1000 < 1000:
|
||||||
dFmt := prefix + decimalFormat(decimals, "µs")
|
dFmt := prefix + suffixDecimalFormat(decimals, "µs")
|
||||||
return fmt.Sprintf(dFmt, d.Seconds()*1000*1000)
|
return fmt.Sprintf(dFmt, d.Seconds()*1000*1000)
|
||||||
// Milliseconds.
|
// Milliseconds.
|
||||||
case d.Seconds()*1000 < 1000:
|
case d.Seconds()*1000 < 1000:
|
||||||
dFmt := prefix + decimalFormat(decimals, "ms")
|
dFmt := prefix + suffixDecimalFormat(decimals, "ms")
|
||||||
return fmt.Sprintf(dFmt, d.Seconds()*1000)
|
return fmt.Sprintf(dFmt, d.Seconds()*1000)
|
||||||
// Seconds.
|
// Seconds.
|
||||||
case d.Seconds() < 60:
|
case d.Seconds() < 60:
|
||||||
dFmt := prefix + decimalFormat(decimals, "s")
|
dFmt := prefix + suffixDecimalFormat(decimals, "s")
|
||||||
return fmt.Sprintf(dFmt, d.Seconds())
|
return fmt.Sprintf(dFmt, d.Seconds())
|
||||||
// Minutes.
|
// Minutes.
|
||||||
case d.Minutes() < 60:
|
case d.Minutes() < 60:
|
||||||
dFmt := prefix + decimalFormat(decimals, "m")
|
dFmt := prefix + suffixDecimalFormat(decimals, "m")
|
||||||
return fmt.Sprintf(dFmt, d.Minutes())
|
return fmt.Sprintf(dFmt, d.Minutes())
|
||||||
// Hours.
|
// Hours.
|
||||||
case d.Hours() < 24:
|
case d.Hours() < 24:
|
||||||
dFmt := prefix + decimalFormat(decimals, "h")
|
dFmt := prefix + suffixDecimalFormat(decimals, "h")
|
||||||
return fmt.Sprintf(dFmt, d.Hours())
|
return fmt.Sprintf(dFmt, d.Hours())
|
||||||
// Days.
|
// Days.
|
||||||
default:
|
default:
|
||||||
dFmt := prefix + decimalFormat(decimals, "d")
|
dFmt := prefix + suffixDecimalFormat(decimals, "d")
|
||||||
return fmt.Sprintf(dFmt, d.Hours()/24)
|
return fmt.Sprintf(dFmt, d.Hours()/24)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func decimalFormat(decimals int, unit string) string {
|
func suffixDecimalFormat(decimals int, suffix string) string {
|
||||||
return fmt.Sprintf("%%.%df%s", decimals, unit)
|
suffix = strings.ReplaceAll(suffix, "%", "%%") // safe `%` character for fmt.
|
||||||
|
return fmt.Sprintf("%%.%df%s", decimals, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueFormatterSingleUnitDuration is a factory to create a custom duration
|
// ValueFormatterSingleUnitDuration is a factory to create a custom duration
|
||||||
@ -103,3 +105,42 @@ func ValueFormatterSingleUnitSeconds(seconds float64) string {
|
|||||||
f := ValueFormatterSingleUnitDuration(time.Second, 0)
|
f := ValueFormatterSingleUnitDuration(time.Second, 0)
|
||||||
return f(seconds)
|
return f(seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValueFormatterRound is a formatter that will receive a float64
|
||||||
|
// value and will round to the nearest value without decimals.
|
||||||
|
func ValueFormatterRound(value float64) string {
|
||||||
|
f := ValueFormatterRoundWithSuffix("")
|
||||||
|
return f(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFormatterRoundWithSuffix is a factory that returns a formatter
|
||||||
|
// that will receive a float64 value and will round to the nearest value
|
||||||
|
// without decimals adding a suffix to the final value string representation.
|
||||||
|
func ValueFormatterRoundWithSuffix(suffix string) ValueFormatter {
|
||||||
|
return valueFormatterSuffixWithTransformer(0, suffix, math.Round)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFormatterSuffix is a factory that returns a formatter
|
||||||
|
// that will receive a float64 value and return a string representation with
|
||||||
|
// the desired number of decimal truncated and a suffix.
|
||||||
|
func ValueFormatterSuffix(decimals int, suffix string) ValueFormatter {
|
||||||
|
return valueFormatterSuffixWithTransformer(decimals, suffix, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueFormatterSuffixWithTransformer is a factory that returns a formatter
|
||||||
|
// that will apply a tranform function to the received value before
|
||||||
|
// returning the decimal with suffix representation.
|
||||||
|
func valueFormatterSuffixWithTransformer(decimals int, suffix string, transformFunc func(float64) float64) ValueFormatter {
|
||||||
|
dFmt := suffixDecimalFormat(decimals, suffix)
|
||||||
|
return func(value float64) string {
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if transformFunc != nil {
|
||||||
|
value = transformFunc(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(dFmt, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -143,6 +143,90 @@ func TestFormatters(t *testing.T) {
|
|||||||
formatter: ValueFormatterSingleUnitDuration(time.Nanosecond, 8),
|
formatter: ValueFormatterSingleUnitDuration(time.Nanosecond, 8),
|
||||||
want: "",
|
want: "",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles NaN values",
|
||||||
|
value: math.NaN(),
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles 0 values",
|
||||||
|
value: 0,
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles > x.5 values",
|
||||||
|
value: 96.7,
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "97",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles < x.5 values",
|
||||||
|
value: 1621.2,
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "1621",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles x.5 values",
|
||||||
|
value: 6.5,
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles minus > x.5 values",
|
||||||
|
value: -96.7,
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "-97",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles minus < x.5 values",
|
||||||
|
value: -1621.2,
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "-1621",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles minus x.5 values",
|
||||||
|
value: -6.5,
|
||||||
|
formatter: ValueFormatterRound,
|
||||||
|
want: "-7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles values with suffix",
|
||||||
|
value: 96.7,
|
||||||
|
formatter: ValueFormatterRoundWithSuffix("km"),
|
||||||
|
want: "97km",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Suffix formatter handles values with decimals",
|
||||||
|
value: 11234567890.71234567890,
|
||||||
|
formatter: ValueFormatterSuffix(4, " reqps"),
|
||||||
|
want: "11234567890.7123 reqps",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Suffix formatter handles NaN values",
|
||||||
|
value: math.NaN(),
|
||||||
|
formatter: ValueFormatterSuffix(2, "test"),
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Suffix formatter handles 0 values",
|
||||||
|
value: 0,
|
||||||
|
formatter: ValueFormatterSuffix(2, "test"),
|
||||||
|
want: "0.00test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Suffix formatters handles correctly percent suffix",
|
||||||
|
value: 96.78,
|
||||||
|
formatter: ValueFormatterSuffix(2, "%"),
|
||||||
|
want: "96.78%",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Round formatter handles values with percent suffix",
|
||||||
|
value: 96.7,
|
||||||
|
formatter: ValueFormatterRoundWithSuffix("%"),
|
||||||
|
want: "97%",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user