write.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package core
  2. import (
  3. "errors"
  4. "fmt"
  5. "reflect"
  6. "strconv"
  7. "strings"
  8. )
  9. // the tag used to denote the name of the question
  10. const tagName = "survey"
  11. // add a few interfaces so users can configure how the prompt values are set
  12. type settable interface {
  13. WriteAnswer(field string, value interface{}) error
  14. }
  15. func WriteAnswer(t interface{}, name string, v interface{}) (err error) {
  16. // if the field is a custom type
  17. if s, ok := t.(settable); ok {
  18. // use the interface method
  19. return s.WriteAnswer(name, v)
  20. }
  21. // the target to write to
  22. target := reflect.ValueOf(t)
  23. // the value to write from
  24. value := reflect.ValueOf(v)
  25. // make sure we are writing to a pointer
  26. if target.Kind() != reflect.Ptr {
  27. return errors.New("you must pass a pointer as the target of a Write operation")
  28. }
  29. // the object "inside" of the target pointer
  30. elem := target.Elem()
  31. // handle the special types
  32. switch elem.Kind() {
  33. // if we are writing to a struct
  34. case reflect.Struct:
  35. // get the name of the field that matches the string we were given
  36. fieldIndex, err := findFieldIndex(elem, name)
  37. // if something went wrong
  38. if err != nil {
  39. // bubble up
  40. return err
  41. }
  42. field := elem.Field(fieldIndex)
  43. // handle references to the settable interface aswell
  44. if s, ok := field.Interface().(settable); ok {
  45. // use the interface method
  46. return s.WriteAnswer(name, v)
  47. }
  48. if field.CanAddr() {
  49. if s, ok := field.Addr().Interface().(settable); ok {
  50. // use the interface method
  51. return s.WriteAnswer(name, v)
  52. }
  53. }
  54. // copy the value over to the normal struct
  55. return copy(field, value)
  56. case reflect.Map:
  57. mapType := reflect.TypeOf(t).Elem()
  58. if mapType.Key().Kind() != reflect.String || mapType.Elem().Kind() != reflect.Interface {
  59. return errors.New("answer maps must be of type map[string]interface")
  60. }
  61. mt := *t.(*map[string]interface{})
  62. mt[name] = value.Interface()
  63. return nil
  64. }
  65. // otherwise just copy the value to the target
  66. return copy(elem, value)
  67. }
  68. // BUG(AlecAivazis): the current implementation might cause weird conflicts if there are
  69. // two fields with same name that only differ by casing.
  70. func findFieldIndex(s reflect.Value, name string) (int, error) {
  71. // the type of the value
  72. sType := s.Type()
  73. // first look for matching tags so we can overwrite matching field names
  74. for i := 0; i < sType.NumField(); i++ {
  75. // the field we are current scanning
  76. field := sType.Field(i)
  77. // the value of the survey tag
  78. tag := field.Tag.Get(tagName)
  79. // if the tag matches the name we are looking for
  80. if tag != "" && tag == name {
  81. // then we found our index
  82. return i, nil
  83. }
  84. }
  85. // then look for matching names
  86. for i := 0; i < sType.NumField(); i++ {
  87. // the field we are current scanning
  88. field := sType.Field(i)
  89. // if the name of the field matches what we're looking for
  90. if strings.ToLower(field.Name) == strings.ToLower(name) {
  91. return i, nil
  92. }
  93. }
  94. // we didn't find the field
  95. return -1, fmt.Errorf("could not find field matching %v", name)
  96. }
  97. // isList returns true if the element is something we can Len()
  98. func isList(v reflect.Value) bool {
  99. switch v.Type().Kind() {
  100. case reflect.Array, reflect.Slice:
  101. return true
  102. default:
  103. return false
  104. }
  105. }
  106. // Write takes a value and copies it to the target
  107. func copy(t reflect.Value, v reflect.Value) (err error) {
  108. // if something ends up panicing we need to catch it in a deferred func
  109. defer func() {
  110. if r := recover(); r != nil {
  111. // if we paniced with an error
  112. if _, ok := r.(error); ok {
  113. // cast the result to an error object
  114. err = r.(error)
  115. } else if _, ok := r.(string); ok {
  116. // otherwise we could have paniced with a string so wrap it in an error
  117. err = errors.New(r.(string))
  118. }
  119. }
  120. }()
  121. // if we are copying from a string result to something else
  122. if v.Kind() == reflect.String && v.Type() != t.Type() {
  123. var castVal interface{}
  124. var casterr error
  125. vString := v.Interface().(string)
  126. switch t.Kind() {
  127. case reflect.Bool:
  128. castVal, casterr = strconv.ParseBool(vString)
  129. case reflect.Int:
  130. castVal, casterr = strconv.Atoi(vString)
  131. case reflect.Int8:
  132. var val64 int64
  133. val64, casterr = strconv.ParseInt(vString, 10, 8)
  134. if casterr == nil {
  135. castVal = int8(val64)
  136. }
  137. case reflect.Int16:
  138. var val64 int64
  139. val64, casterr = strconv.ParseInt(vString, 10, 16)
  140. if casterr == nil {
  141. castVal = int16(val64)
  142. }
  143. case reflect.Int32:
  144. var val64 int64
  145. val64, casterr = strconv.ParseInt(vString, 10, 32)
  146. if casterr == nil {
  147. castVal = int32(val64)
  148. }
  149. case reflect.Int64:
  150. castVal, casterr = strconv.ParseInt(vString, 10, 64)
  151. case reflect.Uint:
  152. var val64 uint64
  153. val64, casterr = strconv.ParseUint(vString, 10, 8)
  154. if casterr == nil {
  155. castVal = uint(val64)
  156. }
  157. case reflect.Uint8:
  158. var val64 uint64
  159. val64, casterr = strconv.ParseUint(vString, 10, 8)
  160. if casterr == nil {
  161. castVal = uint8(val64)
  162. }
  163. case reflect.Uint16:
  164. var val64 uint64
  165. val64, casterr = strconv.ParseUint(vString, 10, 16)
  166. if casterr == nil {
  167. castVal = uint16(val64)
  168. }
  169. case reflect.Uint32:
  170. var val64 uint64
  171. val64, casterr = strconv.ParseUint(vString, 10, 32)
  172. if casterr == nil {
  173. castVal = uint32(val64)
  174. }
  175. case reflect.Uint64:
  176. castVal, casterr = strconv.ParseUint(vString, 10, 64)
  177. case reflect.Float32:
  178. var val64 float64
  179. val64, casterr = strconv.ParseFloat(vString, 32)
  180. if casterr == nil {
  181. castVal = float32(val64)
  182. }
  183. case reflect.Float64:
  184. castVal, casterr = strconv.ParseFloat(vString, 64)
  185. default:
  186. return fmt.Errorf("Unable to convert from string to type %s", t.Kind())
  187. }
  188. if casterr != nil {
  189. return casterr
  190. }
  191. t.Set(reflect.ValueOf(castVal))
  192. return
  193. }
  194. // if we are copying from one slice or array to another
  195. if isList(v) && isList(t) {
  196. // loop over every item in the desired value
  197. for i := 0; i < v.Len(); i++ {
  198. // write to the target given its kind
  199. switch t.Kind() {
  200. // if its a slice
  201. case reflect.Slice:
  202. // an object of the correct type
  203. obj := reflect.Indirect(reflect.New(t.Type().Elem()))
  204. // write the appropriate value to the obj and catch any errors
  205. if err := copy(obj, v.Index(i)); err != nil {
  206. return err
  207. }
  208. // just append the value to the end
  209. t.Set(reflect.Append(t, obj))
  210. // otherwise it could be an array
  211. case reflect.Array:
  212. // set the index to the appropriate value
  213. copy(t.Slice(i, i+1).Index(0), v.Index(i))
  214. }
  215. }
  216. } else {
  217. // set the value to the target
  218. t.Set(v)
  219. }
  220. // we're done
  221. return
  222. }