forked from gorilla/schema
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
1,169 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are | ||
met: | ||
|
||
* Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above | ||
copyright notice, this list of conditions and the following disclaimer | ||
in the documentation and/or other materials provided with the | ||
distribution. | ||
* Neither the name of Google Inc. nor the names of its | ||
contributors may be used to endorse or promote products derived from | ||
this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// Copyright 2012 The Gorilla Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package schema | ||
|
||
import ( | ||
"errors" | ||
"reflect" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
var invalidPath = errors.New("schema: invalid path") | ||
|
||
// newCache returns a new cache. | ||
func newCache() *cache { | ||
c := cache{ | ||
m: make(map[reflect.Type]*structInfo), | ||
conv: make(map[reflect.Type]Converter), | ||
} | ||
for k, v := range converters { | ||
c.conv[k] = v | ||
} | ||
return &c | ||
} | ||
|
||
// cache caches meta-data about a struct. | ||
type cache struct { | ||
l sync.Mutex | ||
m map[reflect.Type]*structInfo | ||
conv map[reflect.Type]Converter | ||
} | ||
|
||
// parsePath parses a path in dotted notation verifying that it is a valid | ||
// path to a struct field. | ||
// | ||
// It returns "path parts" which contain indices to fields to be used by | ||
// reflect.Value.FieldByIndex(). Multiple parts are required for slices of | ||
// structs. | ||
func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) { | ||
var struc *structInfo | ||
var field *fieldInfo | ||
var index64 int64 | ||
var err error | ||
parts := make([]pathPart, 0) | ||
path := make([]int, 0) | ||
keys := strings.Split(p, ".") | ||
for i := 0; i < len(keys); i++ { | ||
if struc = c.get(t); struc == nil { | ||
return nil, invalidPath | ||
} | ||
if field = struc.get(keys[i]); field == nil { | ||
return nil, invalidPath | ||
} | ||
// Valid field. Append index. | ||
path = append(path, field.idx) | ||
if field.ss { | ||
// Parse a special case: slices of structs. | ||
// i+1 must be the slice index, and i+2 must exist. | ||
i++ | ||
if i+1 >= len(keys) { | ||
return nil, invalidPath | ||
} | ||
if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil { | ||
return nil, invalidPath | ||
} | ||
parts = append(parts, pathPart{ | ||
path: path, | ||
field: field, | ||
index: int(index64), | ||
}) | ||
path = make([]int, 0) | ||
|
||
// Get the next struct type, dropping ptrs. | ||
if field.typ.Kind() == reflect.Ptr { | ||
t = field.typ.Elem() | ||
} else { | ||
t = field.typ | ||
} | ||
if t.Kind() == reflect.Slice { | ||
t = t.Elem() | ||
if t.Kind() == reflect.Ptr { | ||
t = t.Elem() | ||
} | ||
} | ||
} else if field.typ.Kind() == reflect.Struct { | ||
t = field.typ | ||
} else if field.typ.Kind() == reflect.Ptr && field.typ.Elem().Kind() == reflect.Struct { | ||
t = field.typ.Elem() | ||
} | ||
} | ||
// Add the remaining. | ||
parts = append(parts, pathPart{ | ||
path: path, | ||
field: field, | ||
index: -1, | ||
}) | ||
return parts, nil | ||
} | ||
|
||
// get returns a cached structInfo, creating it if necessary. | ||
func (c *cache) get(t reflect.Type) *structInfo { | ||
c.l.Lock() | ||
info := c.m[t] | ||
c.l.Unlock() | ||
if info == nil { | ||
info = c.create(t) | ||
c.l.Lock() | ||
c.m[t] = info | ||
c.l.Unlock() | ||
} | ||
return info | ||
} | ||
|
||
// creat creates a structInfo with meta-data about a struct. | ||
func (c *cache) create(t reflect.Type) *structInfo { | ||
info := &structInfo{fields: make(map[string]*fieldInfo)} | ||
for i := 0; i < t.NumField(); i++ { | ||
field := t.Field(i) | ||
alias := fieldAlias(field) | ||
if alias == "-" { | ||
// Ignore this field. | ||
continue | ||
} | ||
// Check if the type is supported and don't cache it if not. | ||
// First let's get the basic type. | ||
isSlice, isStruct := false, false | ||
ft := field.Type | ||
if ft.Kind() == reflect.Ptr { | ||
ft = ft.Elem() | ||
} | ||
if isSlice = ft.Kind() == reflect.Slice; isSlice { | ||
ft = ft.Elem() | ||
if ft.Kind() == reflect.Ptr { | ||
ft = ft.Elem() | ||
} | ||
} | ||
if isStruct = ft.Kind() == reflect.Struct; !isStruct { | ||
if conv := c.conv[ft]; conv == nil { | ||
// Type is not supported. | ||
continue | ||
} | ||
} | ||
info.fields[alias] = &fieldInfo{ | ||
idx: i, | ||
typ: field.Type, | ||
ss: isSlice && isStruct, | ||
} | ||
} | ||
return info | ||
} | ||
|
||
// ---------------------------------------------------------------------------- | ||
|
||
type structInfo struct { | ||
fields map[string]*fieldInfo | ||
} | ||
|
||
func (i *structInfo) get(alias string) *fieldInfo { | ||
return i.fields[alias] | ||
} | ||
|
||
type fieldInfo struct { | ||
typ reflect.Type | ||
idx int // field index in the struct. | ||
ss bool // true if this is a slice of structs. | ||
} | ||
|
||
type pathPart struct { | ||
field *fieldInfo | ||
path []int // path to the field: walks structs using field indices. | ||
index int // struct index in slices of structs. | ||
} | ||
|
||
// ---------------------------------------------------------------------------- | ||
|
||
// fieldAlias parses a field tag to get a field alias. | ||
func fieldAlias(field reflect.StructField) string { | ||
var alias string | ||
if tag := field.Tag.Get("schema"); tag != "" { | ||
// For now tags only support the name but let's folow the | ||
// comma convention from encoding/json and others. | ||
if idx := strings.Index(tag, ","); idx == -1 { | ||
alias = tag | ||
} else { | ||
alias = tag[:idx] | ||
} | ||
} | ||
if alias == "" { | ||
alias = field.Name | ||
} | ||
return alias | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// Copyright 2012 The Gorilla Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package schema | ||
|
||
import ( | ||
"reflect" | ||
"strconv" | ||
) | ||
|
||
type Converter func(string) reflect.Value | ||
|
||
var ( | ||
invalidValue = reflect.Value{} | ||
boolType = reflect.TypeOf(false) | ||
float32Type = reflect.TypeOf(float32(0)) | ||
float64Type = reflect.TypeOf(float64(0)) | ||
intType = reflect.TypeOf(int(0)) | ||
int8Type = reflect.TypeOf(int8(0)) | ||
int16Type = reflect.TypeOf(int16(0)) | ||
int32Type = reflect.TypeOf(int32(0)) | ||
int64Type = reflect.TypeOf(int64(0)) | ||
stringType = reflect.TypeOf("") | ||
uintType = reflect.TypeOf(uint(0)) | ||
uint8Type = reflect.TypeOf(uint8(0)) | ||
uint16Type = reflect.TypeOf(uint16(0)) | ||
uint32Type = reflect.TypeOf(uint32(0)) | ||
uint64Type = reflect.TypeOf(uint64(0)) | ||
) | ||
|
||
// Default converters for basic types. | ||
var converters = map[reflect.Type]Converter{ | ||
boolType: convertBool, | ||
float32Type: convertFloat32, | ||
float64Type: convertFloat64, | ||
intType: convertInt, | ||
int8Type: convertInt8, | ||
int16Type: convertInt16, | ||
int32Type: convertInt32, | ||
int64Type: convertInt64, | ||
stringType: convertString, | ||
uintType: convertUint, | ||
uint8Type: convertUint8, | ||
uint16Type: convertUint16, | ||
uint32Type: convertUint32, | ||
uint64Type: convertUint64, | ||
} | ||
|
||
func convertBool(value string) reflect.Value { | ||
if v, err := strconv.ParseBool(value); err == nil { | ||
return reflect.ValueOf(v) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertFloat32(value string) reflect.Value { | ||
if v, err := strconv.ParseFloat(value, 32); err == nil { | ||
return reflect.ValueOf(float32(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertFloat64(value string) reflect.Value { | ||
if v, err := strconv.ParseFloat(value, 64); err == nil { | ||
return reflect.ValueOf(v) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertInt(value string) reflect.Value { | ||
if v, err := strconv.ParseInt(value, 10, 0); err == nil { | ||
return reflect.ValueOf(int(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertInt8(value string) reflect.Value { | ||
if v, err := strconv.ParseInt(value, 10, 8); err == nil { | ||
return reflect.ValueOf(int8(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertInt16(value string) reflect.Value { | ||
if v, err := strconv.ParseInt(value, 10, 16); err == nil { | ||
return reflect.ValueOf(int16(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertInt32(value string) reflect.Value { | ||
if v, err := strconv.ParseInt(value, 10, 32); err == nil { | ||
return reflect.ValueOf(int32(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertInt64(value string) reflect.Value { | ||
if v, err := strconv.ParseInt(value, 10, 64); err == nil { | ||
return reflect.ValueOf(v) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertString(value string) reflect.Value { | ||
return reflect.ValueOf(value) | ||
} | ||
|
||
func convertUint(value string) reflect.Value { | ||
if v, err := strconv.ParseUint(value, 10, 0); err == nil { | ||
return reflect.ValueOf(uint(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertUint8(value string) reflect.Value { | ||
if v, err := strconv.ParseUint(value, 10, 8); err == nil { | ||
return reflect.ValueOf(uint8(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertUint16(value string) reflect.Value { | ||
if v, err := strconv.ParseUint(value, 10, 16); err == nil { | ||
return reflect.ValueOf(uint16(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertUint32(value string) reflect.Value { | ||
if v, err := strconv.ParseUint(value, 10, 32); err == nil { | ||
return reflect.ValueOf(uint32(v)) | ||
} | ||
return invalidValue | ||
} | ||
|
||
func convertUint64(value string) reflect.Value { | ||
if v, err := strconv.ParseUint(value, 10, 64); err == nil { | ||
return reflect.ValueOf(v) | ||
} | ||
return invalidValue | ||
} |
Oops, something went wrong.