From 4c8d5213dcde49962097b97f06a8be3f3c25f035 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Thu, 20 Oct 2016 14:14:10 +0100 Subject: [PATCH] Add double FIELD TYPE and improve type checking --- sql/sql_test.go | 12 +++++- sql/type.go | 2 +- util/conv/conv.go | 100 ++++++++++++++++++++++++++++++++++++---------- util/item/each.go | 11 +++++ 4 files changed, 101 insertions(+), 24 deletions(-) diff --git a/sql/sql_test.go b/sql/sql_test.go index 55126f1f..9360a867 100644 --- a/sql/sql_test.go +++ b/sql/sql_test.go @@ -1413,11 +1413,11 @@ func Test_Parse_Queries_Define(t *testing.T) { }, { sql: `DEFINE FIELD temp ON person TYPE`, - err: "Found `` but expected `any, url, uuid, color, email, phone, array, object, domain, string, number, custom, boolean, datetime, latitude, longitude`", + err: "Found `` but expected `any, url, uuid, color, email, phone, array, object, domain, string, number, double, custom, boolean, datetime, latitude, longitude`", }, { sql: `DEFINE FIELD temp ON person TYPE something`, - err: "Found `something` but expected `any, url, uuid, color, email, phone, array, object, domain, string, number, custom, boolean, datetime, latitude, longitude`", + err: "Found `something` but expected `any, url, uuid, color, email, phone, array, object, domain, string, number, double, custom, boolean, datetime, latitude, longitude`", }, { sql: `DEFINE FIELD temp ON person TYPE any`, @@ -1483,6 +1483,14 @@ func Test_Parse_Queries_Define(t *testing.T) { Type: "number", }}}, }, + { + sql: `DEFINE FIELD temp ON person TYPE double`, + res: &Query{Statements: []Statement{&DefineFieldStatement{ + Name: "temp", + What: []string{"person"}, + Type: "double", + }}}, + }, { sql: `DEFINE FIELD temp ON person TYPE custom`, res: &Query{Statements: []Statement{&DefineFieldStatement{ diff --git a/sql/type.go b/sql/type.go index 52f4af32..2b071003 100644 --- a/sql/type.go +++ b/sql/type.go @@ -16,7 +16,7 @@ package sql func (p *parser) parseType() (exp string, err error) { - allowed := []string{"any", "url", "uuid", "color", "email", "phone", "array", "object", "domain", "string", "number", "custom", "boolean", "datetime", "latitude", "longitude"} + allowed := []string{"any", "url", "uuid", "color", "email", "phone", "array", "object", "domain", "string", "number", "double", "custom", "boolean", "datetime", "latitude", "longitude"} _, lit, err := p.shouldBe(IDENT, CUSTOM) if err != nil { diff --git a/util/conv/conv.go b/util/conv/conv.go index ba82eaea..41b3cec9 100644 --- a/util/conv/conv.go +++ b/util/conv/conv.go @@ -16,13 +16,40 @@ package conv import ( "fmt" + "strconv" "time" "github.com/asaskevich/govalidator" ) +func toNumber(str string) (int64, error) { + val, err := strconv.ParseFloat(str, 64) + if err != nil { + val = 0.0 + } + return int64(val), err +} + +func toDouble(str string) (float64, error) { + val, err := strconv.ParseFloat(str, 64) + if err != nil { + val = 0.0 + } + return float64(val), err +} + +func toBoolean(str string) (bool, error) { + val, err := strconv.ParseBool(str) + if err != nil { + val = false + } + return bool(val), err +} + +// -------------------------------------------------- + func ConvertToUrl(obj interface{}) (val string, err error) { - val = govalidator.ToString(obj) + val = fmt.Sprintf("%v", obj) if !govalidator.IsURL(val) { err = fmt.Errorf("Not a valid url") } @@ -30,7 +57,7 @@ func ConvertToUrl(obj interface{}) (val string, err error) { } func ConvertToUuid(obj interface{}) (val string, err error) { - val = govalidator.ToString(obj) + val = fmt.Sprintf("%v", obj) if !govalidator.IsUUID(val) { err = fmt.Errorf("Not a valid uuid") } @@ -38,7 +65,7 @@ func ConvertToUuid(obj interface{}) (val string, err error) { } func ConvertToEmail(obj interface{}) (val string, err error) { - val = govalidator.ToString(obj) + val = fmt.Sprintf("%v", obj) if !govalidator.IsEmail(val) { err = fmt.Errorf("Not a valid email") } @@ -46,7 +73,7 @@ func ConvertToEmail(obj interface{}) (val string, err error) { } func ConvertToPhone(obj interface{}) (val string, err error) { - val = govalidator.ToString(obj) + val = fmt.Sprintf("%v", obj) if !govalidator.Matches(val, `^[\s\d\+\-\(\)]+$`) { err = fmt.Errorf("Not a valid phone") } @@ -54,7 +81,7 @@ func ConvertToPhone(obj interface{}) (val string, err error) { } func ConvertToColor(obj interface{}) (val string, err error) { - val = govalidator.ToString(obj) + val = fmt.Sprintf("%v", obj) if !govalidator.IsHexcolor(val) && !govalidator.IsRGBcolor(val) { err = fmt.Errorf("Not a valid color") } @@ -80,7 +107,7 @@ func ConvertToObject(obj interface{}) (val map[string]interface{}, err error) { } func ConvertToDomain(obj interface{}) (val string, err error) { - val = govalidator.ToString(obj) + val = fmt.Sprintf("%v", obj) if !govalidator.IsDNSName(val) { err = fmt.Errorf("Not a valid domain name") } @@ -88,7 +115,7 @@ func ConvertToDomain(obj interface{}) (val string, err error) { } func ConvertToBase64(obj interface{}) (val string, err error) { - val = govalidator.ToString(obj) + val = fmt.Sprintf("%v", obj) if !govalidator.IsBase64(val) { err = fmt.Errorf("Not valid base64 data") } @@ -96,24 +123,55 @@ func ConvertToBase64(obj interface{}) (val string, err error) { } func ConvertToString(obj interface{}) (val string, err error) { - if now, ok := obj.(string); ok { + switch now := obj.(type) { + case string: return now, err + case []interface{}: + return val, fmt.Errorf("Not valid string") + case map[string]interface{}: + return val, fmt.Errorf("Not valid string") + default: + return fmt.Sprintf("%v", obj), err } - return govalidator.ToString(obj), err } -func ConvertToNumber(obj interface{}) (val float64, err error) { - if now, ok := obj.(float64); ok { - return now, err +func ConvertToNumber(obj interface{}) (val int64, err error) { + switch now := obj.(type) { + case int64: + return int64(now), err + case float64: + return int64(now), err + case string: + return toNumber(now) + default: + return toNumber(fmt.Sprintf("%v", obj)) + } +} + +func ConvertToDouble(obj interface{}) (val float64, err error) { + switch now := obj.(type) { + case int64: + return float64(now), err + case float64: + return float64(now), err + case string: + return toDouble(now) + default: + return toDouble(fmt.Sprintf("%v", obj)) } - return govalidator.ToFloat(govalidator.ToString(obj)) } func ConvertToBoolean(obj interface{}) (val bool, err error) { - if now, ok := obj.(bool); ok { - return now, err + switch now := obj.(type) { + case int64: + return now > 0, err + case float64: + return now > 0, err + case string: + return toBoolean(now) + default: + return toBoolean(fmt.Sprintf("%v", obj)) } - return govalidator.ToBoolean(govalidator.ToString(obj)) } func ConvertToDatetime(obj interface{}) (val time.Time, err error) { @@ -126,7 +184,7 @@ func ConvertToDatetime(obj interface{}) (val time.Time, err error) { } func ConvertToLatitude(obj interface{}) (val float64, err error) { - str := govalidator.ToString(obj) + str := fmt.Sprintf("%v", obj) if !govalidator.IsLatitude(str) { err = fmt.Errorf("Not a valid latitude") } @@ -134,8 +192,8 @@ func ConvertToLatitude(obj interface{}) (val float64, err error) { } func ConvertToLongitude(obj interface{}) (val float64, err error) { - str := govalidator.ToString(obj) - if !govalidator.IsLatitude(str) { + str := fmt.Sprintf("%v", obj) + if !govalidator.IsLongitude(str) { err = fmt.Errorf("Not a valid longitude") } return govalidator.ToFloat(str) @@ -145,10 +203,10 @@ func ConvertToOneOf(obj interface{}, pos ...interface{}) (val interface{}, err e for _, now := range pos { if num, ok := obj.(int64); ok { if float64(num) == now { - return obj, nil + return obj, err } } else if obj == now { - return obj, nil + return obj, err } } return nil, fmt.Errorf("Not a valid option") diff --git a/util/item/each.go b/util/item/each.go index 651c710a..61ff5338 100644 --- a/util/item/each.go +++ b/util/item/each.go @@ -237,6 +237,17 @@ func each(fld *sql.DefineFieldStatement, initial *data.Doc, current *data.Doc) ( } } + case "double": + if val, err := conv.ConvertToDouble(c); err == nil { + current.Set(val, fld.Name) + } else { + if fld.Validate { + return fmt.Errorf("Field '%v' needs to be a double", fld.Name) + } else { + current.Iff(i, fld.Name) + } + } + case "boolean": if val, err := conv.ConvertToBoolean(c); err == nil { current.Set(val, fld.Name)