termdash/widgets/linechart/value_formatter.go

147 lines
4.9 KiB
Go

// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linechart
// value_formatter.go provides common implementations of ValueFormatter that can be
// used with the YAxisFormattedValues() LineChart option.
import (
"fmt"
"math"
"strings"
"time"
)
// durationSingleUnitPrettyFormat returns the pretty format in one single
// unit for a time.Duration, the different returned unit formats
// are: nanoseconds, microseconds, milliseconds, seconds, minutes
// hours, days.
func durationSingleUnitPrettyFormat(d time.Duration, decimals int) string {
// Check if the duration is less than 0.
prefix := ""
if d < 0 {
prefix = "-"
d = time.Duration(math.Abs(d.Seconds()) * float64(time.Second))
}
switch {
// Nanoseconds.
case d.Nanoseconds() < 1000:
dFmt := prefix + "%dns"
return fmt.Sprintf(dFmt, d.Nanoseconds())
// Microseconds.
case d.Seconds()*1000*1000 < 1000:
dFmt := prefix + suffixDecimalFormat(decimals, "µs")
return fmt.Sprintf(dFmt, d.Seconds()*1000*1000)
// Milliseconds.
case d.Seconds()*1000 < 1000:
dFmt := prefix + suffixDecimalFormat(decimals, "ms")
return fmt.Sprintf(dFmt, d.Seconds()*1000)
// Seconds.
case d.Seconds() < 60:
dFmt := prefix + suffixDecimalFormat(decimals, "s")
return fmt.Sprintf(dFmt, d.Seconds())
// Minutes.
case d.Minutes() < 60:
dFmt := prefix + suffixDecimalFormat(decimals, "m")
return fmt.Sprintf(dFmt, d.Minutes())
// Hours.
case d.Hours() < 24:
dFmt := prefix + suffixDecimalFormat(decimals, "h")
return fmt.Sprintf(dFmt, d.Hours())
// Days.
default:
dFmt := prefix + suffixDecimalFormat(decimals, "d")
return fmt.Sprintf(dFmt, d.Hours()/24)
}
}
func suffixDecimalFormat(decimals int, suffix string) string {
suffix = strings.Replace(suffix, "%", "%%", -1) // Safe `%` character for fmt.
return fmt.Sprintf("%%.%df%s", decimals, suffix)
}
// ValueFormatterSingleUnitDuration is a factory to create a custom duration
// in a single unit representation formatter based on a unit and the decimals
// to truncate.
// If the received decimal value is negative it will fallback to a 0 decimal
// value.
// The result value formatter handles NaN values, if the value formatter
// receives a NaN float64 it will return an empty string.
func ValueFormatterSingleUnitDuration(unit time.Duration, decimals int) ValueFormatter {
if decimals < 0 {
decimals = 0
}
return func(v float64) string {
if math.IsNaN(v) {
return ""
}
d := time.Duration(v * float64(unit))
return durationSingleUnitPrettyFormat(d, decimals)
}
}
// ValueFormatterSingleUnitSeconds is a formatter that will receive
// seconds unit in the float64 argument and will return a pretty
// format in one single unit without decimals, it doesn't round,
// it truncates.
// Received seconds that are NaN will be ignored and return an
// empty string.
func ValueFormatterSingleUnitSeconds(seconds float64) string {
f := ValueFormatterSingleUnitDuration(time.Second, 0)
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 transform 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)
}
}