helpers.go raw

   1  /**
   2   * Copyright 2016 IBM Corp.
   3   *
   4   * Licensed under the Apache License, Version 2.0 (the "License");
   5   * you may not use this file except in compliance with the License.
   6   * You may obtain a copy of the License at
   7   *
   8   *    http://www.apache.org/licenses/LICENSE-2.0
   9   *
  10   * Unless required by applicable law or agreed to in writing, software
  11   * distributed under the License is distributed on an "AS IS" BASIS,
  12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13   * See the License for the specific language governing permissions and
  14   * limitations under the License.
  15   */
  16  
  17  // Package sl has convenience functions for returning pointers to values
  18  package sl
  19  
  20  import (
  21  	"reflect"
  22  	"strings"
  23  	"time"
  24  
  25  	"github.com/softlayer/softlayer-go/datatypes"
  26  )
  27  
  28  // Int returns a pointer to the int value provided
  29  func Int(v int) *int {
  30  	return &v
  31  }
  32  
  33  // Uint returns a pointer to the uint value provided
  34  func Uint(v uint) *uint {
  35  	return &v
  36  }
  37  
  38  // String returns a pointer to the string value provided
  39  func String(v string) *string {
  40  	return &v
  41  }
  42  
  43  // Bool returns a pointer to the bool value provided
  44  func Bool(v bool) *bool {
  45  	return &v
  46  }
  47  
  48  // Time converts the time.Time value provided to a datatypes.Time value,
  49  // and returns a pointer to it
  50  func Time(v time.Time) *datatypes.Time {
  51  	r := datatypes.Time{Time: v}
  52  	return &r
  53  }
  54  
  55  // Float converts the float value provided to a datatypes.Float64 value,
  56  // and returns a pointer to it
  57  func Float(v float64) *datatypes.Float64 {
  58  	r := datatypes.Float64(v)
  59  	return &r
  60  }
  61  
  62  // Convenience functions to simplify dereference of datatype properties
  63  
  64  // Get returns the value of p, either p itself, or, if p is a pointer, the
  65  // value that p points to. d is an optional default value to be returned
  66  // in the event that p is nil. If d is not specified, and p is nil, a
  67  // type-appropriate zero-value is returned instead.
  68  func Get(p interface{}, d ...interface{}) interface{} {
  69  	var (
  70  		val interface{}
  71  		ok  bool
  72  	)
  73  
  74  	if val, ok = GetOk(p); ok {
  75  		return val
  76  	}
  77  
  78  	if len(d) > 0 {
  79  		return d[0]
  80  	}
  81  
  82  	return val
  83  }
  84  
  85  // GetOk returns the value of p, either p itself, or, if p is a pointer, the
  86  // value that p points to. If p is nil, a type-appropriate zero-value is
  87  // returned instead. If p is a value or non-nil pointer, the second return
  88  // value will be true.  Otherwise, it will be false.
  89  func GetOk(p interface{}) (interface{}, bool) {
  90  	t := reflect.TypeOf(p)
  91  
  92  	// if p is a non-pointer, just return it
  93  	if t.Kind() != reflect.Ptr {
  94  		return p, true
  95  	}
  96  
  97  	// p is a pointer.  If non-nil, return the value pointed to
  98  	v := reflect.Indirect(reflect.ValueOf(p))
  99  	if v.IsValid() {
 100  		return v.Interface(), true
 101  	}
 102  
 103  	// p is a nil pointer.  Return the zero value for the pointed-to type
 104  	return reflect.Zero(t.Elem()).Interface(), false
 105  }
 106  
 107  // Grab returns the value specified by the path given,
 108  // starting from the struct s.
 109  // If at any point in the path the lookup falls short
 110  // (i.e. a field is not found), or if the last field in the path is nil
 111  // itself, a type-appropriate zero-value is returned.
 112  // This behavior can be overidden by providing a default value.
 113  //
 114  // This is useful for getting values our of deeply nested structures
 115  // Example: val := sl.Grab(virtualGuest, "Datacenter.Name")
 116  func Grab(s interface{}, path string, d ...interface{}) interface{} {
 117  	var (
 118  		val interface{}
 119  		ok  bool
 120  	)
 121  
 122  	if val, ok = GrabOk(s, path); ok {
 123  		return val
 124  	}
 125  
 126  	if len(d) > 0 {
 127  		return d[0]
 128  	}
 129  
 130  	return val
 131  }
 132  
 133  // GrabOk returns the value specified by the path given,
 134  // starting from the struct s.
 135  // If at any point in the path the lookup falls short
 136  // (i.e. a field is not found), or if the last field in the path is nil
 137  // itself, a type-appropriate zero-value is returned.
 138  // It returns a second value, a boolean, which will be false if it failed
 139  // to lookup the value, including if the last field in the path was nil.
 140  //
 141  // This is useful for getting values our of deeply nested structures
 142  // Example: val, ok := sl.GrabOk(virtualGuest, "Datacenter.Name")
 143  func GrabOk(s interface{}, path string) (interface{}, bool) {
 144  	t := reflect.TypeOf(s)
 145  	if t.Kind() != reflect.Struct {
 146  		return nil, false
 147  	}
 148  
 149  	dotIndex := strings.Index(path, ".")
 150  	if dotIndex == -1 {
 151  		dotIndex = len(path)
 152  	}
 153  
 154  	fieldName := path[0:dotIndex]
 155  	val := reflect.ValueOf(s)
 156  	fieldVal := val.FieldByName(fieldName)
 157  	if fieldVal.Kind() == reflect.Ptr {
 158  		if fieldVal.IsNil() {
 159  			return reflect.Zero(fieldVal.Type().Elem()).Interface(), false
 160  		}
 161  
 162  		fieldVal = reflect.Indirect(fieldVal)
 163  	}
 164  
 165  	result, ok := GetOk(fieldVal.Interface())
 166  	if !ok {
 167  		return result, ok
 168  	}
 169  
 170  	if dotIndex == len(path) {
 171  		return result, ok
 172  	}
 173  
 174  	return GrabOk(result, path[dotIndex+1:len(path)])
 175  }
 176