mirror of https://github.com/mum4k/termdash.git
Use variadic args for Value factory options
Signed-off-by: Xabier Larrakoetxea <slok69@gmail.com>
This commit is contained in:
parent
a8931e2820
commit
c1bf776dba
|
@ -73,7 +73,7 @@ type YProperties struct {
|
||||||
// ScaleMode determines how the Y axis scales.
|
// ScaleMode determines how the Y axis scales.
|
||||||
ScaleMode YScaleMode
|
ScaleMode YScaleMode
|
||||||
// ValueFormatter is the formatter used to format numeric values to string representation.
|
// ValueFormatter is the formatter used to format numeric values to string representation.
|
||||||
ValueFormatter valueFormatter
|
ValueFormatter func(float64) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewYDetails retrieves details about the Y axis required to draw it on a
|
// NewYDetails retrieves details about the Y axis required to draw it on a
|
||||||
|
|
|
@ -213,8 +213,8 @@ func TestY(t *testing.T) {
|
||||||
End: image.Point{1, 2},
|
End: image.Point{1, 2},
|
||||||
Scale: mustNewYScale(0, 3, 2, nonZeroDecimals, YScaleModeAnchored, testValueFormatter),
|
Scale: mustNewYScale(0, 3, 2, nonZeroDecimals, YScaleModeAnchored, testValueFormatter),
|
||||||
Labels: []*Label{
|
Labels: []*Label{
|
||||||
{NewFormattedValue(0, nonZeroDecimals, testValueFormatter), image.Point{0, 1}},
|
{NewValue(0, nonZeroDecimals, ValueFormatter(testValueFormatter)), image.Point{0, 1}},
|
||||||
{NewFormattedValue(1.72, nonZeroDecimals, testValueFormatter), image.Point{0, 0}},
|
{NewValue(1.72, nonZeroDecimals, ValueFormatter(testValueFormatter)), image.Point{0, 0}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -69,7 +69,7 @@ type YScale struct {
|
||||||
|
|
||||||
// valueFormatter is the value formatter used for the labels
|
// valueFormatter is the value formatter used for the labels
|
||||||
// represented by the values on the scale.
|
// represented by the values on the scale.
|
||||||
valueFormatter valueFormatter
|
valueFormatter func(float64) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements fmt.Stringer.
|
// String implements fmt.Stringer.
|
||||||
|
@ -82,7 +82,7 @@ func (ys *YScale) String() string {
|
||||||
// calculated scale, see NewValue for details.
|
// calculated scale, see NewValue for details.
|
||||||
// Max must be greater or equal to min. The graphHeight must be a positive
|
// Max must be greater or equal to min. The graphHeight must be a positive
|
||||||
// number.
|
// number.
|
||||||
func NewYScale(min, max float64, graphHeight, nonZeroDecimals int, mode YScaleMode, valueFormatter valueFormatter) (*YScale, error) {
|
func NewYScale(min, max float64, graphHeight, nonZeroDecimals int, mode YScaleMode, valueFormatter func(float64) string) (*YScale, error) {
|
||||||
if max < min {
|
if max < min {
|
||||||
return nil, fmt.Errorf("max(%v) cannot be less than min(%v)", max, min)
|
return nil, fmt.Errorf("max(%v) cannot be less than min(%v)", max, min)
|
||||||
}
|
}
|
||||||
|
@ -199,12 +199,13 @@ func (ys *YScale) CellLabel(y int) (*Value, error) {
|
||||||
// yScaleNewValue is a helper method to get new values for the y scale
|
// yScaleNewValue is a helper method to get new values for the y scale
|
||||||
// that selects the correct value factory method depending on the passed
|
// that selects the correct value factory method depending on the passed
|
||||||
// arguments.
|
// arguments.
|
||||||
func yScaleNewValue(value float64, nonZeroDecimals int, valueFormatter valueFormatter) *Value {
|
func yScaleNewValue(value float64, nonZeroDecimals int, valueFormatter func(float64) string) *Value {
|
||||||
|
opts := []ValueOption{}
|
||||||
if valueFormatter != nil {
|
if valueFormatter != nil {
|
||||||
return NewFormattedValue(value, nonZeroDecimals, valueFormatter)
|
opts = append(opts, ValueFormatter(valueFormatter))
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewValue(value, nonZeroDecimals)
|
return NewValue(value, nonZeroDecimals, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XScale is the scale of the X axis.
|
// XScale is the scale of the X axis.
|
||||||
|
|
|
@ -22,8 +22,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// mustNewYScale returns a new YScale or panics.
|
// mustNewYScale returns a new YScale or panics.
|
||||||
func mustNewYScale(min, max float64, graphHeight, nonZeroDecimals int, mode YScaleMode, formatter valueFormatter) *YScale {
|
func mustNewYScale(min, max float64, graphHeight, nonZeroDecimals int, mode YScaleMode, valueFormatter func(float64) string) *YScale {
|
||||||
s, err := NewYScale(min, max, graphHeight, nonZeroDecimals, mode, formatter)
|
s, err := NewYScale(min, max, graphHeight, nonZeroDecimals, mode, valueFormatter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,30 @@ import (
|
||||||
"github.com/mum4k/termdash/internal/numbers"
|
"github.com/mum4k/termdash/internal/numbers"
|
||||||
)
|
)
|
||||||
|
|
||||||
type valueFormatter = func(float64) string
|
// ValueOption is used to provide options to the NewValue function.
|
||||||
|
type ValueOption interface {
|
||||||
|
// set sets the provided option.
|
||||||
|
set(*valueOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueOptions struct {
|
||||||
|
formatter func(v float64) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueOption implements ValueOption.
|
||||||
|
type valueOption func(opts *valueOptions)
|
||||||
|
|
||||||
|
// set implements ValueOption.set.
|
||||||
|
func (vo valueOption) set(opts *valueOptions) {
|
||||||
|
vo(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueFormatter sets a custom formatter for the value.
|
||||||
|
func ValueFormatter(formatter func(float64) string) ValueOption {
|
||||||
|
return valueOption(func(opts *valueOptions) {
|
||||||
|
opts.formatter = formatter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Value represents one value.
|
// Value represents one value.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
|
@ -38,10 +61,10 @@ type Value struct {
|
||||||
// NonZeroDecimals indicates the rounding precision used, it is provided on
|
// NonZeroDecimals indicates the rounding precision used, it is provided on
|
||||||
// a call to newValue.
|
// a call to newValue.
|
||||||
NonZeroDecimals int
|
NonZeroDecimals int
|
||||||
// Formatter will format value to a string representation of the value,
|
|
||||||
// if Formatter is not present it will fallback to default format.
|
|
||||||
Formatter valueFormatter
|
|
||||||
|
|
||||||
|
// formatter will format value to a string representation of the value,
|
||||||
|
// if Formatter is not present it will fallback to default format.
|
||||||
|
formatter func(float64) string
|
||||||
// text value if this value was constructed using NewTextValue.
|
// text value if this value was constructed using NewTextValue.
|
||||||
text string
|
text string
|
||||||
}
|
}
|
||||||
|
@ -53,20 +76,19 @@ func (v *Value) String() string {
|
||||||
|
|
||||||
// NewValue returns a new instance representing the provided value, rounding
|
// NewValue returns a new instance representing the provided value, rounding
|
||||||
// the value up to the specified number of non-zero decimal places.
|
// the value up to the specified number of non-zero decimal places.
|
||||||
func NewValue(v float64, nonZeroDecimals int) *Value {
|
func NewValue(v float64, nonZeroDecimals int, opts ...ValueOption) *Value {
|
||||||
return NewFormattedValue(v, nonZeroDecimals, nil)
|
opt := &valueOptions{}
|
||||||
}
|
for _, o := range opts {
|
||||||
|
o.set(opt)
|
||||||
|
}
|
||||||
|
|
||||||
// NewFormattedValue returns a new instance representing the provided value,
|
|
||||||
// using a value formatter.
|
|
||||||
func NewFormattedValue(v float64, nonZeroDecimals int, formatter valueFormatter) *Value {
|
|
||||||
r, zd := numbers.RoundToNonZeroPlaces(v, nonZeroDecimals)
|
r, zd := numbers.RoundToNonZeroPlaces(v, nonZeroDecimals)
|
||||||
return &Value{
|
return &Value{
|
||||||
Value: v,
|
Value: v,
|
||||||
Rounded: r,
|
Rounded: r,
|
||||||
ZeroDecimals: zd,
|
ZeroDecimals: zd,
|
||||||
NonZeroDecimals: nonZeroDecimals,
|
NonZeroDecimals: nonZeroDecimals,
|
||||||
Formatter: formatter,
|
formatter: opt.formatter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,19 +107,19 @@ func (v *Value) Text() string {
|
||||||
return v.text
|
return v.text
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Formatter != nil {
|
if v.formatter != nil {
|
||||||
return v.Formatter(v.Value)
|
return v.formatter(v.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.defaultFormatter(v.Rounded)
|
return defaultFormatter(v.Rounded, v.NonZeroDecimals, v.ZeroDecimals)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Value) defaultFormatter(value float64) string {
|
func defaultFormatter(value float64, nonZeroDecimals, zeroDecimals int) string {
|
||||||
if math.Ceil(value) == value {
|
if math.Ceil(value) == value {
|
||||||
return fmt.Sprintf("%.0f", value)
|
return fmt.Sprintf("%.0f", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
format := fmt.Sprintf("%%.%df", v.NonZeroDecimals+v.ZeroDecimals)
|
format := fmt.Sprintf("%%.%df", nonZeroDecimals+zeroDecimals)
|
||||||
t := fmt.Sprintf(format, value)
|
t := fmt.Sprintf(format, value)
|
||||||
if len(t) > 10 {
|
if len(t) > 10 {
|
||||||
t = fmt.Sprintf("%.2e", value)
|
t = fmt.Sprintf("%.2e", value)
|
||||||
|
|
|
@ -22,10 +22,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValue(t *testing.T) {
|
func TestValue(t *testing.T) {
|
||||||
|
formatter := func(float64) string { return "test" }
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
float float64
|
float float64
|
||||||
nonZeroDecimals int
|
nonZeroDecimals int
|
||||||
|
formatter func(float64) string
|
||||||
want *Value
|
want *Value
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -61,11 +64,24 @@ func TestValue(t *testing.T) {
|
||||||
NonZeroDecimals: 0,
|
NonZeroDecimals: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "formatter value when value formatter as option",
|
||||||
|
float: 1.01234,
|
||||||
|
nonZeroDecimals: 0,
|
||||||
|
formatter: formatter,
|
||||||
|
want: &Value{
|
||||||
|
Value: 1.01234,
|
||||||
|
Rounded: 1.01234,
|
||||||
|
ZeroDecimals: 1,
|
||||||
|
NonZeroDecimals: 0,
|
||||||
|
formatter: formatter,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
got := NewValue(tc.float, tc.nonZeroDecimals)
|
got := NewValue(tc.float, tc.nonZeroDecimals, ValueFormatter(tc.formatter))
|
||||||
if diff := pretty.Compare(tc.want, got); diff != "" {
|
if diff := pretty.Compare(tc.want, got); diff != "" {
|
||||||
t.Errorf("NewValue => unexpected diff (-want, +got):\n%s", diff)
|
t.Errorf("NewValue => unexpected diff (-want, +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
|
@ -124,16 +140,3 @@ func TestNewTextValue(t *testing.T) {
|
||||||
t.Errorf("v.Text => got %q, want %q", got, want)
|
t.Errorf("v.Text => got %q, want %q", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFormattedValue(t *testing.T) {
|
|
||||||
const (
|
|
||||||
value = 42
|
|
||||||
want = "test"
|
|
||||||
)
|
|
||||||
|
|
||||||
v := NewFormattedValue(value, 2, func(float64) string { return "test" })
|
|
||||||
got := v.Text()
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("v.Text => got %q, want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue