Created
January 31, 2015 02:20
-
-
Save nelsam/d34ca88b91b02fa5ea71 to your computer and use it in GitHub Desktop.
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
package datastore | |
import ( | |
"errors" | |
"reflect" | |
"github.com/coopernurse/gorp" | |
) | |
// DbColumnStorer is a type that can be stored as a column's value | |
// in the database. DbColumnStorers are usually also DbColumnReaders. | |
type DbColumnStorer interface { | |
// DbColumnValue should return the value that will represent the | |
// DbColumnStorer in a column in the database. | |
DbColumnValue() interface{} | |
} | |
// DbColumnReader is a type that can load data from a database | |
// column, doing all necessary type conversions to convert the | |
// database column's value to the DbColumnReader's type. | |
// DbColumnReaders are usually also DbColumnStorers. | |
type DbColumnReader interface { | |
// DbColumnTypePtr should return a pointer to a value of the type | |
// that the DbColumnReader's data is stored as. For example, if | |
// the database column representation of this DbColumnReader is an | |
// int, then DbColumnTypePtr() should return new(int) | |
DbColumnTypePtr() interface{} | |
// FromDbColumnValue should take the database representation of | |
// this DbColumnReader and convert it to the final Go value, | |
// returning an error if conversion fails at any point. | |
FromDbColumnValue(interface{}) error | |
} | |
// DbColumnConverter includes all methods from both DbColumnStorer and | |
// DbColumnReader, mostly for convenience when testing whether or not | |
// types implement these methods. | |
type DbColumnConverter interface { | |
DbColumnStorer | |
DbColumnReader | |
} | |
// DbColumnConverterTypeConverter is a gorp.TypeConverter that converts | |
// any values matching the DbColumnConverter interface. | |
type DbColumnConverterTypeConverter struct { | |
} | |
// ToDb will attempt to typecast the passed in value to DbColumnConverter | |
// and return the result of calling DbColumnValue() on that value. If the | |
// passed in value does not successfully typecast to DbColumnConverter, it | |
// will instead just return the passed in value. | |
func (converter DbColumnConverterTypeConverter) ToDb(value interface{}) (interface{}, error) { | |
refValue := reflect.ValueOf(value) | |
if refValue.Kind() == reflect.Ptr && refValue.IsNil() { | |
return nil, nil | |
} | |
if converter, ok := value.(DbColumnStorer); ok { | |
return converter.DbColumnValue(), nil | |
} | |
return value, nil | |
} | |
// FromDb will attempt to typecast the passed in target to DbColumnConverter and | |
// return a CustomScanner that uses the DbColumnConverter's DbColumnTypePtr and | |
// FromDbColumnValue methods to convert from the database value. If the passed | |
// in target does not successfully typecast to DbColumnConverter, it returns an | |
// empty scanner and false. | |
func (converter DbColumnConverterTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) { | |
scanner := gorp.CustomScanner{} | |
convert := true | |
targetIsPtr := false | |
targetVal := reflect.ValueOf(target) | |
// target will always be a pointer to the actual target, which | |
// itself could be a pointer. | |
if targetVal.Elem().Kind() == reflect.Ptr { | |
targetIsPtr = true | |
targetVal = targetVal.Elem() | |
if targetVal.IsNil() { | |
targetVal.Set(reflect.New(targetVal.Type().Elem())) | |
} | |
target = targetVal.Interface() | |
} | |
switch converter := target.(type) { | |
case DbColumnReader: | |
scanner.Target = converter | |
holder := converter.DbColumnTypePtr() | |
holderVal := reflect.ValueOf(holder) | |
newHolder := reflect.New(holderVal.Type()) | |
scanner.Holder = newHolder.Interface() | |
scanner.Binder = func(interface{}, interface{}) error { | |
holderVal = newHolder.Elem() | |
if targetIsPtr { | |
// If the DB value is nil, set the value to nil and | |
// return; otherwise, initialize the pointer. | |
if holderVal.IsNil() { | |
if !targetVal.IsNil() { | |
targetVal.Set(reflect.Zero(targetVal.Type())) | |
} | |
return nil | |
} | |
} else if holderVal.IsNil() { | |
return errors.New("Non-pointer types cannot be nil") | |
} | |
return converter.FromDbColumnValue(holderVal.Interface()) | |
} | |
default: | |
convert = false | |
} | |
return scanner, convert | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment