diff --git a/compose/service/values/sanitizer.go b/compose/service/values/sanitizer.go index 133195f29..b8010c4b1 100644 --- a/compose/service/values/sanitizer.go +++ b/compose/service/values/sanitizer.go @@ -193,53 +193,30 @@ func (sanitizer) sDatetime(v *types.RecordValue, f *types.ModuleField, m *types. return v } +// sNumber sanitizes func (sanitizer) sNumber(v *types.RecordValue, f *types.ModuleField, m *types.Module) *types.RecordValue { - // No point in continuing - if v.Value == "" || f.Options == nil { - return v - } - - // Default to 0 for consistency - if f.Options["precision"] == nil || f.Options["precision"] == "" { - f.Options["precision"] = 0 - } - - // Since Options are not structured, there would appear that there can be a bit of a mess - // when it comes to types,so this is needed. - var prec float64 - unk := f.Options["precision"] - switch i := unk.(type) { - case float64: - prec = i - case int: - prec = float64(i) - case int64: - prec = float64(i) - case string: - pp, err := strconv.ParseFloat(i, 64) - if err != nil { - prec = 0 - break - } - prec = pp - } - - // Clamp between 0 and 6; this was originally done in corteza-js so we keep it here. - if prec < 0 { - prec = 0 - } - if prec > 6 { - prec = 6 - } - base, err := strconv.ParseFloat(v.Value, 64) if err != nil { + v.Value = "0" return v } - // 1. Format the value to the desired precision - // 2. In case of fractures, remove trailing 0's - v.Value = strconv.FormatFloat(base, 'f', int(prec), 64) + // calculate percision + var p = 0 + if f.Options != nil { + p = int(f.Options.Int64(fieldOpt_Number_precision)) + + if p < fieldOpt_Number_precision_min { + p = fieldOpt_Number_precision_min + } else if p > fieldOpt_Number_precision_max { + p = fieldOpt_Number_precision_max + } + } + + // Format the value to the desired precision + v.Value = strconv.FormatFloat(base, 'f', p, 64) + + // In case of fractures, remove trailing 0's if strings.Contains(v.Value, ".") { v.Value = strings.TrimRight(v.Value, "0") } diff --git a/compose/service/values/sanitizer_test.go b/compose/service/values/sanitizer_test.go index 38ee0579c..f5196e064 100644 --- a/compose/service/values/sanitizer_test.go +++ b/compose/service/values/sanitizer_test.go @@ -90,6 +90,24 @@ func Test_sanitizer_Run(t *testing.T) { input: "2020-03-11T11:20:08.471Z", output: "2020-03-11T11:20:08Z", }, + { + name: "number space trim", + kind: "Number", + input: " 42 ", + output: "42", + }, + { + name: "number negative", + kind: "Number", + input: "-42", + output: "-42", + }, + { + name: "number positive", + kind: "Number", + input: "+42", + output: "42", + }, { name: "number precision", kind: "Number", diff --git a/compose/service/values/shared.go b/compose/service/values/shared.go index 829190d0d..d9888c9a3 100644 --- a/compose/service/values/shared.go +++ b/compose/service/values/shared.go @@ -18,7 +18,9 @@ const ( fieldOpt_Datetime_onlyFutureValues = "onlyFutureValues" fieldOpt_Datetime_onlyPastValues = "onlyPastValues" - fieldOpt_Number_precision = "precision" + fieldOpt_Number_precision = "precision" + fieldOpt_Number_precision_min = 0 + fieldOpt_Number_precision_max = 6 fieldOpt_Url_onlySecure = "onlySecure" ) diff --git a/compose/types/module_field_options.go b/compose/types/module_field_options.go index 2d356b2fd..1b28346ca 100644 --- a/compose/types/module_field_options.go +++ b/compose/types/module_field_options.go @@ -3,7 +3,9 @@ package types import ( "database/sql/driver" "encoding/json" + "fmt" "github.com/pkg/errors" + "strconv" ) type ( @@ -52,12 +54,28 @@ func (opt ModuleFieldOptions) Int64(key string) int64 { } func (opt ModuleFieldOptions) Int64Def(key string, def int64) int64 { - if _, has := opt[key]; has { - if n, ok := opt[key].(int64); ok { - return n - } - } + if val, has := opt[key]; has { + switch conv := val.(type) { + case int: + return int64(conv) + case int64: + return conv + default: + // to avoid covering every possible type, just convert value into string + strVal := fmt.Sprintf("%v", val) + fmt.Printf("\n[%v] key: %s => (%T) %s\n\n", opt, key, val, strVal) + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } + + if floatVal, err := strconv.ParseFloat(strVal, 64); err == nil { + return int64(floatVal) + } + + } + + } return def } diff --git a/compose/types/module_field_options_test.go b/compose/types/module_field_options_test.go new file mode 100644 index 000000000..a1ade3a60 --- /dev/null +++ b/compose/types/module_field_options_test.go @@ -0,0 +1,33 @@ +package types + +import ( + "testing" +) + +func TestModuleFieldOptions_Int64Def(t *testing.T) { + tests := []struct { + name string + opt ModuleFieldOptions + key string + def int64 + want int64 + }{ + // TODO: Add test cases. + {"unexisting", ModuleFieldOptions{}, "k", 42, 42}, + {"nil", ModuleFieldOptions{"k": nil}, "k", 42, 42}, + {"bool", ModuleFieldOptions{"k": true}, "k", 42, 42}, + {"int", ModuleFieldOptions{"k": 1}, "k", 42, 1}, + {"float", ModuleFieldOptions{"k": 1.00000000001}, "k", 42, 1}, + {"stringed-int", ModuleFieldOptions{"k": "1"}, "k", 42, 1}, + {"stringed-float-1", ModuleFieldOptions{"k": "1.0"}, "k", 42, 1}, + {"stringed-float-2", ModuleFieldOptions{"k": "1.01"}, "k", 42, 1}, + {"stringed-float-3", ModuleFieldOptions{"k": "1.00000000001"}, "k", 42, 1}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.opt.Int64Def(tt.key, tt.def); got != tt.want { + t.Errorf("Int64Def() = %v, want %v", got, tt.want) + } + }) + } +}