summaryrefslogtreecommitdiff
path: root/vendor/github.com/ulule
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ulule')
-rw-r--r--vendor/github.com/ulule/deepcopier/LICENSE22
-rw-r--r--vendor/github.com/ulule/deepcopier/README.md129
-rw-r--r--vendor/github.com/ulule/deepcopier/deepcopier.go350
3 files changed, 501 insertions, 0 deletions
diff --git a/vendor/github.com/ulule/deepcopier/LICENSE b/vendor/github.com/ulule/deepcopier/LICENSE
new file mode 100644
index 000000000..d5c4ea02c
--- /dev/null
+++ b/vendor/github.com/ulule/deepcopier/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Ulule
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/ulule/deepcopier/README.md b/vendor/github.com/ulule/deepcopier/README.md
new file mode 100644
index 000000000..25aafae8a
--- /dev/null
+++ b/vendor/github.com/ulule/deepcopier/README.md
@@ -0,0 +1,129 @@
+# Deepcopier
+
+[![Build Status](https://secure.travis-ci.org/ulule/deepcopier.png?branch=master)](http://travis-ci.org/ulule/deepcopier)
+
+This package is meant to make copying of structs to/from others structs a bit easier.
+
+## Installation
+
+```bash
+go get -u github.com/ulule/deepcopier
+```
+
+## Usage
+
+```golang
+// Deep copy instance1 into instance2
+Copy(instance1).To(instance2)
+
+// Deep copy instance1 into instance2 and passes the following context (which
+// is basically a map[string]interface{}) as first argument
+// to methods of instance2 that defined the struct tag "context".
+Copy(instance1).WithContext(map[string]interface{}{"foo": "bar"}).To(instance2)
+
+// Deep copy instance2 into instance1
+Copy(instance1).From(instance2)
+
+// Deep copy instance2 into instance1 and passes the following context (which
+// is basically a map[string]interface{}) as first argument
+// to methods of instance1 that defined the struct tag "context".
+Copy(instance1).WithContext(map[string]interface{}{"foo": "bar"}).From(instance2)
+```
+
+Available options for `deepcopier` struct tag:
+
+| Option | Description |
+| --------- | -------------------------------------------------------------------- |
+| `field` | Field or method name in source instance |
+| `skip` | Ignores the field |
+| `context` | Takes a `map[string]interface{}` as first argument (for methods) |
+| `force` | Set the value of a `sql.Null*` field (instead of copying the struct) |
+
+**Options example:**
+
+```golang
+type Source struct {
+ Name string
+ SkipMe string
+ SQLNullStringToSQLNullString sql.NullString
+ SQLNullStringToString sql.NullString
+
+}
+
+func (Source) MethodThatTakesContext(c map[string]interface{}) string {
+ return "whatever"
+}
+
+type Destination struct {
+ FieldWithAnotherNameInSource string `deepcopier:"field:Name"`
+ SkipMe string `deepcopier:"skip"`
+ MethodThatTakesContext string `deepcopier:"context"`
+ SQLNullStringToSQLNullString sql.NullString
+ SQLNullStringToString string `deepcopier:"force"`
+}
+
+```
+
+Example:
+
+```golang
+package main
+
+import (
+ "fmt"
+
+ "github.com/ulule/deepcopier"
+)
+
+// Model
+type User struct {
+ // Basic string field
+ Name string
+ // Deepcopier supports https://golang.org/pkg/database/sql/driver/#Valuer
+ Email sql.NullString
+}
+
+func (u *User) MethodThatTakesContext(ctx map[string]interface{}) string {
+ // do whatever you want
+ return "hello from this method"
+}
+
+// Resource
+type UserResource struct {
+ DisplayName string `deepcopier:"field:Name"`
+ SkipMe string `deepcopier:"skip"`
+ MethodThatTakesContext string `deepcopier:"context"`
+ Email string `deepcopier:"force"`
+
+}
+
+func main() {
+ user := &User{
+ Name: "gilles",
+ Email: sql.NullString{
+ Valid: true,
+ String: "gilles@example.com",
+ },
+ }
+
+ resource := &UserResource{}
+
+ deepcopier.Copy(user).To(resource)
+
+ fmt.Println(resource.DisplayName)
+ fmt.Println(resource.Email)
+}
+```
+
+Looking for more information about the usage?
+
+We wrote [an introduction article](https://github.com/ulule/deepcopier/blob/master/examples/rest-usage/README.rst).
+Have a look and feel free to give us your feedback.
+
+## Contributing
+
+* Ping us on twitter [@oibafsellig](https://twitter.com/oibafsellig), [@thoas](https://twitter.com/thoas)
+* Fork the [project](https://github.com/ulule/deepcopier)
+* Help us improving and fixing [issues](https://github.com/ulule/deepcopier/issues)
+
+Don't hesitate ;)
diff --git a/vendor/github.com/ulule/deepcopier/deepcopier.go b/vendor/github.com/ulule/deepcopier/deepcopier.go
new file mode 100644
index 000000000..8cccb5b78
--- /dev/null
+++ b/vendor/github.com/ulule/deepcopier/deepcopier.go
@@ -0,0 +1,350 @@
+package deepcopier
+
+import (
+ "database/sql/driver"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+const (
+ // TagName is the deepcopier struct tag name.
+ TagName = "deepcopier"
+ // FieldOptionName is the from field option name for struct tag.
+ FieldOptionName = "field"
+ // ContextOptionName is the context option name for struct tag.
+ ContextOptionName = "context"
+ // SkipOptionName is the skip option name for struct tag.
+ SkipOptionName = "skip"
+ // ForceOptionName is the skip option name for struct tag.
+ ForceOptionName = "force"
+)
+
+type (
+ // TagOptions is a map that contains extracted struct tag options.
+ TagOptions map[string]string
+
+ // Options are copier options.
+ Options struct {
+ // Context given to WithContext() method.
+ Context map[string]interface{}
+ // Reversed reverses struct tag checkings.
+ Reversed bool
+ }
+)
+
+// DeepCopier deep copies a struct to/from a struct.
+type DeepCopier struct {
+ dst interface{}
+ src interface{}
+ ctx map[string]interface{}
+}
+
+// Copy sets source or destination.
+func Copy(src interface{}) *DeepCopier {
+ return &DeepCopier{src: src}
+}
+
+// WithContext injects the given context into the builder instance.
+func (dc *DeepCopier) WithContext(ctx map[string]interface{}) *DeepCopier {
+ dc.ctx = ctx
+ return dc
+}
+
+// To sets the destination.
+func (dc *DeepCopier) To(dst interface{}) error {
+ dc.dst = dst
+ return process(dc.dst, dc.src, Options{Context: dc.ctx})
+}
+
+// From sets the given the source as destination and destination as source.
+func (dc *DeepCopier) From(src interface{}) error {
+ dc.dst = dc.src
+ dc.src = src
+ return process(dc.dst, dc.src, Options{Context: dc.ctx, Reversed: true})
+}
+
+// process handles copy.
+func process(dst interface{}, src interface{}, args ...Options) error {
+ var (
+ options = Options{}
+ srcValue = reflect.Indirect(reflect.ValueOf(src))
+ dstValue = reflect.Indirect(reflect.ValueOf(dst))
+ srcFieldNames = getFieldNames(src)
+ srcMethodNames = getMethodNames(src)
+ )
+
+ if len(args) > 0 {
+ options = args[0]
+ }
+
+ if !dstValue.CanAddr() {
+ return fmt.Errorf("destination %+v is unaddressable", dstValue.Interface())
+ }
+
+ for _, f := range srcFieldNames {
+ var (
+ srcFieldValue = srcValue.FieldByName(f)
+ srcFieldType, srcFieldFound = srcValue.Type().FieldByName(f)
+ srcFieldName = srcFieldType.Name
+ dstFieldName = srcFieldName
+ tagOptions TagOptions
+ )
+
+ if !srcFieldFound {
+ continue
+ }
+
+ if options.Reversed {
+ tagOptions = getTagOptions(srcFieldType.Tag.Get(TagName))
+ if v, ok := tagOptions[FieldOptionName]; ok && v != "" {
+ dstFieldName = v
+ }
+ } else {
+ if name, opts := getRelatedField(dst, srcFieldName); name != "" {
+ dstFieldName, tagOptions = name, opts
+ }
+ }
+
+ if _, ok := tagOptions[SkipOptionName]; ok {
+ continue
+ }
+
+ var (
+ dstFieldType, dstFieldFound = dstValue.Type().FieldByName(dstFieldName)
+ dstFieldValue = dstValue.FieldByName(dstFieldName)
+ )
+
+ if !dstFieldFound {
+ continue
+ }
+
+ // Force option for empty interfaces and nullable types
+ _, force := tagOptions[ForceOptionName]
+
+ // Valuer -> ptr
+ if isNullableType(srcFieldType.Type) && dstFieldValue.Kind() == reflect.Ptr && force {
+ v, _ := srcFieldValue.Interface().(driver.Valuer).Value()
+ if v == nil {
+ continue
+ }
+
+ valueType := reflect.TypeOf(v)
+
+ ptr := reflect.New(valueType)
+ ptr.Elem().Set(reflect.ValueOf(v))
+
+ if valueType.AssignableTo(dstFieldType.Type.Elem()) {
+ dstFieldValue.Set(ptr)
+ }
+
+ continue
+ }
+
+ // Valuer -> value
+ if isNullableType(srcFieldType.Type) {
+ if force {
+ v, _ := srcFieldValue.Interface().(driver.Valuer).Value()
+ if v == nil {
+ continue
+ }
+
+ rv := reflect.ValueOf(v)
+ if rv.Type().AssignableTo(dstFieldType.Type) {
+ dstFieldValue.Set(rv)
+ }
+ }
+
+ continue
+ }
+
+ if dstFieldValue.Kind() == reflect.Interface {
+ if force {
+ dstFieldValue.Set(srcFieldValue)
+ }
+ continue
+ }
+
+ // Ptr -> Value
+ if srcFieldType.Type.Kind() == reflect.Ptr && !srcFieldValue.IsNil() && dstFieldType.Type.Kind() != reflect.Ptr {
+ indirect := reflect.Indirect(srcFieldValue)
+
+ if indirect.Type().AssignableTo(dstFieldType.Type) {
+ dstFieldValue.Set(indirect)
+ continue
+ }
+ }
+
+ // Other types
+ if srcFieldType.Type.AssignableTo(dstFieldType.Type) {
+ dstFieldValue.Set(srcFieldValue)
+ }
+ }
+
+ for _, m := range srcMethodNames {
+ name, opts := getRelatedField(dst, m)
+ if name == "" {
+ continue
+ }
+
+ if _, ok := opts[SkipOptionName]; ok {
+ continue
+ }
+
+ method := reflect.ValueOf(src).MethodByName(m)
+ if !method.IsValid() {
+ return fmt.Errorf("method %s is invalid", m)
+ }
+
+ var (
+ dstFieldType, _ = dstValue.Type().FieldByName(name)
+ dstFieldValue = dstValue.FieldByName(name)
+ _, withContext = opts[ContextOptionName]
+ _, force = opts[ForceOptionName]
+ )
+
+ args := []reflect.Value{}
+ if withContext {
+ args = []reflect.Value{reflect.ValueOf(options.Context)}
+ }
+
+ var (
+ result = method.Call(args)[0]
+ resultInterface = result.Interface()
+ resultValue = reflect.ValueOf(resultInterface)
+ resultType = resultValue.Type()
+ )
+
+ // Value -> Ptr
+ if dstFieldValue.Kind() == reflect.Ptr && force {
+ ptr := reflect.New(resultType)
+ ptr.Elem().Set(resultValue)
+
+ if ptr.Type().AssignableTo(dstFieldType.Type) {
+ dstFieldValue.Set(ptr)
+ }
+
+ continue
+ }
+
+ // Ptr -> value
+ if resultValue.Kind() == reflect.Ptr && force {
+ if resultValue.Elem().Type().AssignableTo(dstFieldType.Type) {
+ dstFieldValue.Set(resultValue.Elem())
+ }
+
+ continue
+ }
+
+ if resultType.AssignableTo(dstFieldType.Type) && result.IsValid() {
+ dstFieldValue.Set(result)
+ }
+ }
+
+ return nil
+}
+
+// getTagOptions parses deepcopier tag field and returns options.
+func getTagOptions(value string) TagOptions {
+ options := TagOptions{}
+
+ for _, opt := range strings.Split(value, ";") {
+ o := strings.Split(opt, ":")
+
+ // deepcopier:"keyword; without; value;"
+ if len(o) == 1 {
+ options[o[0]] = ""
+ }
+
+ // deepcopier:"key:value; anotherkey:anothervalue"
+ if len(o) == 2 {
+ options[strings.TrimSpace(o[0])] = strings.TrimSpace(o[1])
+ }
+ }
+
+ return options
+}
+
+// getRelatedField returns first matching field.
+func getRelatedField(instance interface{}, name string) (string, TagOptions) {
+ var (
+ value = reflect.Indirect(reflect.ValueOf(instance))
+ fieldName string
+ tagOptions TagOptions
+ )
+
+ for i := 0; i < value.NumField(); i++ {
+ var (
+ vField = value.Field(i)
+ tField = value.Type().Field(i)
+ tagOptions = getTagOptions(tField.Tag.Get(TagName))
+ )
+
+ if tField.Type.Kind() == reflect.Struct && tField.Anonymous {
+ if n, o := getRelatedField(vField.Interface(), name); n != "" {
+ return n, o
+ }
+ }
+
+ if v, ok := tagOptions[FieldOptionName]; ok && v == name {
+ return tField.Name, tagOptions
+ }
+
+ if tField.Name == name {
+ return tField.Name, tagOptions
+ }
+ }
+
+ return fieldName, tagOptions
+}
+
+// getMethodNames returns instance's method names.
+func getMethodNames(instance interface{}) []string {
+ var methods []string
+
+ t := reflect.TypeOf(instance)
+ for i := 0; i < t.NumMethod(); i++ {
+ methods = append(methods, t.Method(i).Name)
+ }
+
+ return methods
+}
+
+// getFieldNames returns instance's field names.
+func getFieldNames(instance interface{}) []string {
+ var (
+ fields []string
+ v = reflect.Indirect(reflect.ValueOf(instance))
+ t = v.Type()
+ )
+
+ if t.Kind() != reflect.Struct {
+ return nil
+ }
+
+ for i := 0; i < v.NumField(); i++ {
+ var (
+ vField = v.Field(i)
+ tField = v.Type().Field(i)
+ )
+
+ // Is exportable?
+ if tField.PkgPath != "" {
+ continue
+ }
+
+ if tField.Type.Kind() == reflect.Struct && tField.Anonymous {
+ fields = append(fields, getFieldNames(vField.Interface())...)
+ continue
+ }
+
+ fields = append(fields, tField.Name)
+ }
+
+ return fields
+}
+
+// isNullableType returns true if the given type is a nullable one.
+func isNullableType(t reflect.Type) bool {
+ return t.ConvertibleTo(reflect.TypeOf((*driver.Valuer)(nil)).Elem())
+}