// InputNamed takes a (generally) user-provided input string and parses it into the out // parameter, which must be settable (this usually means you'll have to pass a pointer // to this function). See the documentation on Parse() for a list of the supported types. // If name is provided, it will be included in any error returned by this function. // Additional constraints might be specified with the tag parameter, the ones currently // supported are: // - required: Marks the field as required, will return an error if it's empty. // - optional: Marks the field as optional, will accept empty values. // - max_length: Sets the maximum length for the input. // - min_length: Sets the minimum length for the input. // - alphanumeric: Requires the input to be only letters and numbers // // Finally, the required parameter indicates if the value should be considered required // or optional in absence of the "required" and "optional" tag fields. func InputNamed(name string, input string, out interface{}, tag *structs.Tag, required bool) error { v, err := types.SettableValue(out) if err != nil { return err } if err := parse(input, v); err != nil { return err } if v.Type().Kind() != reflect.Bool && tag != nil { if ((required && !tag.Optional()) || tag.Required()) && input == "" { return RequiredInputError(name) } if maxlen, ok := tag.MaxLength(); ok && len(input) > maxlen { if name != "" { return i18n.Errorfc("form", "%s is too long (maximum length is %d)", name, maxlen) } return i18n.Errorfc("form", "too long (maximum length is %d)", maxlen) } if minlen, ok := tag.MinLength(); ok && len(input) < minlen { if name != "" { return i18n.Errorfc("form", "%s is too short (minimum length is %d)", name, minlen) } return i18n.Errorfc("form", "too short (minimum length is %d)", minlen) } if tag.Alphanumeric() && len(input) > 0 && !alphanumericRe.MatchString(input) { if name != "" { return i18n.Errorfc("form", "%s must be alphanumeric", name) } return i18n.Errorfc("form", "must be alphanumeric") } } return nil }
func (b *Backend) FieldType(typ reflect.Type, t *structs.Tag) (string, error) { if c := codec.FromTag(t); c != nil { if c.Binary || t.PipeName() != "" { return "BLOB", nil } return "TEXT", nil } var ft string switch typ.Kind() { case reflect.Bool: ft = "BOOL" case reflect.Int8: ft = "TINYINT" case reflect.Uint8: ft = "TINYINT UNSIGNED" case reflect.Int16: ft = "SMALLINT" case reflect.Uint16: ft = "SMALLINT UNSIGNED" case reflect.Int32: ft = "INT" case reflect.Uint32: ft = "INT UNSIGNED" case reflect.Int, reflect.Int64: ft = "BIGINT" case reflect.Uint, reflect.Uint64: ft = "BIGINT UNSIGNED" case reflect.Float32: ft = "FLOAT" case reflect.Float64: ft = "DOUBLE" case reflect.String: if ml, ok := t.MaxLength(); ok { ft = fmt.Sprintf("VARCHAR (%d)", ml) } else if fl, ok := t.Length(); ok { ft = fmt.Sprintf("CHAR (%d)", fl) } else { ft = "TEXT" } case reflect.Slice: etyp := typ.Elem() if etyp.Kind() == reflect.Uint8 { // []byte ft = "BLOB" } case reflect.Struct: if typ.Name() == "Time" && typ.PkgPath() == "time" { ft = "DATETIME" } } if ft != "" { return ft, nil } return "", fmt.Errorf("can't map field type %v to a database type", typ) }
func (b *Backend) FieldType(typ reflect.Type, t *structs.Tag) (string, error) { if c := codec.FromTag(t); c != nil { // TODO: Use type JSON on Postgresql >= 9.2 for JSON encoded fields if c.Binary || t.PipeName() != "" { return "BYTEA", nil } return "TEXT", nil } var ft string switch typ.Kind() { case reflect.Bool: ft = "BOOL" case reflect.Int8, reflect.Uint8, reflect.Int16: ft = "INT2" case reflect.Uint16, reflect.Int32: ft = "INT4" case reflect.Int, reflect.Uint, reflect.Uint32, reflect.Int64, reflect.Uint64: ft = "INT8" case reflect.Float32: ft = "FLOAT4" case reflect.Float64: ft = "FLOAT8" case reflect.String: if t.Has("macaddr") { ft = "MACADDR" } else if t.Has("inet") { ft = "INET" } else { if ml, ok := t.MaxLength(); ok { ft = fmt.Sprintf("VARCHAR (%d)", ml) } else if fl, ok := t.Length(); ok { ft = fmt.Sprintf("CHAR (%d)", fl) } else { ft = "TEXT" } } case reflect.Slice: etyp := typ.Elem() if etyp.Kind() == reflect.Uint8 { // []byte ft = "BYTEA" // TODO: database/sql does not support array types. Enable this code // if that changes in the future // } else if typ.Elem().Kind() != reflect.Struct { // et, err := b.FieldType(typ.Elem(), tag) // if err != nil { // return "", err // } // t = et + "[]" } case reflect.Struct: if typ.Name() == "Time" && typ.PkgPath() == "time" { ft = "TIMESTAMP WITHOUT TIME ZONE" } } if t.Has("auto_increment") { if strings.HasPrefix(ft, "INT") { ft = strings.Replace(ft, "INT", "SERIAL", -1) } else { return "", fmt.Errorf("postgres does not support auto incrementing %v", typ) } } if ft != "" { return ft, nil } return "", fmt.Errorf("can't map field type %v to a database type", typ) }