Use variadic args for Value factory options

Signed-off-by: Xabier Larrakoetxea <slok69@gmail.com>
This commit is contained in:
Xabier Larrakoetxea 2019-05-03 07:14:01 +02:00
parent a8931e2820
commit c1bf776dba
No known key found for this signature in database
GPG Key ID: FDAD7FD8275E1B32
6 changed files with 66 additions and 40 deletions

View File

@ -73,7 +73,7 @@ type YProperties struct {
// ScaleMode determines how the Y axis scales.
ScaleMode YScaleMode
// 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

View File

@ -213,8 +213,8 @@ func TestY(t *testing.T) {
End: image.Point{1, 2},
Scale: mustNewYScale(0, 3, 2, nonZeroDecimals, YScaleModeAnchored, testValueFormatter),
Labels: []*Label{
{NewFormattedValue(0, nonZeroDecimals, testValueFormatter), image.Point{0, 1}},
{NewFormattedValue(1.72, nonZeroDecimals, testValueFormatter), image.Point{0, 0}},
{NewValue(0, nonZeroDecimals, ValueFormatter(testValueFormatter)), image.Point{0, 1}},
{NewValue(1.72, nonZeroDecimals, ValueFormatter(testValueFormatter)), image.Point{0, 0}},
},
},
},

View File

@ -69,7 +69,7 @@ type YScale struct {
// valueFormatter is the value formatter used for the labels
// represented by the values on the scale.
valueFormatter valueFormatter
valueFormatter func(float64) string
}
// String implements fmt.Stringer.
@ -82,7 +82,7 @@ func (ys *YScale) String() string {
// calculated scale, see NewValue for details.
// Max must be greater or equal to min. The graphHeight must be a positive
// 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 {
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
// that selects the correct value factory method depending on the passed
// 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 {
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.

View File

@ -22,8 +22,8 @@ import (
)
// mustNewYScale returns a new YScale or panics.
func mustNewYScale(min, max float64, graphHeight, nonZeroDecimals int, mode YScaleMode, formatter valueFormatter) *YScale {
s, err := NewYScale(min, max, graphHeight, nonZeroDecimals, mode, formatter)
func mustNewYScale(min, max float64, graphHeight, nonZeroDecimals int, mode YScaleMode, valueFormatter func(float64) string) *YScale {
s, err := NewYScale(min, max, graphHeight, nonZeroDecimals, mode, valueFormatter)
if err != nil {
panic(err)
}

View File

@ -23,7 +23,30 @@ import (
"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.
type Value struct {
@ -38,10 +61,10 @@ type Value struct {
// NonZeroDecimals indicates the rounding precision used, it is provided on
// a call to newValue.
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 string
}
@ -53,20 +76,19 @@ func (v *Value) String() string {
// NewValue returns a new instance representing the provided value, rounding
// the value up to the specified number of non-zero decimal places.
func NewValue(v float64, nonZeroDecimals int) *Value {
return NewFormattedValue(v, nonZeroDecimals, nil)
}
func NewValue(v float64, nonZeroDecimals int, opts ...ValueOption) *Value {
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)
return &Value{
Value: v,
Rounded: r,
ZeroDecimals: zd,
NonZeroDecimals: nonZeroDecimals,
Formatter: formatter,
formatter: opt.formatter,
}
}
@ -85,19 +107,19 @@ func (v *Value) Text() string {
return v.text
}
if v.Formatter != nil {
return v.Formatter(v.Value)
if v.formatter != nil {
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 {
return fmt.Sprintf("%.0f", value)
}
format := fmt.Sprintf("%%.%df", v.NonZeroDecimals+v.ZeroDecimals)
format := fmt.Sprintf("%%.%df", nonZeroDecimals+zeroDecimals)
t := fmt.Sprintf(format, value)
if len(t) > 10 {
t = fmt.Sprintf("%.2e", value)

View File

@ -22,10 +22,13 @@ import (
)
func TestValue(t *testing.T) {
formatter := func(float64) string { return "test" }
tests := []struct {
desc string
float float64
nonZeroDecimals int
formatter func(float64) string
want *Value
}{
{
@ -61,11 +64,24 @@ func TestValue(t *testing.T) {
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 {
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 != "" {
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)
}
}
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)
}
}