write.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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. func findFieldIndex(s reflect.Value, name string) (int, error) {
  69. // the type of the value
  70. sType := s.Type()
  71. // first look for matching tags so we can overwrite matching field names
  72. for i := 0; i < sType.NumField(); i++ {
  73. // the field we are current scanning
  74. field := sType.Field(i)
  75. // the value of the survey tag
  76. tag := field.Tag.Get(tagName)
  77. // if the tag matches the name we are looking for
  78. if tag != "" && tag == name {
  79. // then we found our index
  80. return i, nil
  81. }
  82. }
  83. // then look for matching names
  84. for i := 0; i < sType.NumField(); i++ {
  85. // the field we are current scanning
  86. field := sType.Field(i)
  87. // if the name of the field matches what we're looking for
  88. if strings.ToLower(field.Name) == strings.ToLower(name) {
  89. return i, nil
  90. }
  91. }
  92. // we didn't find the field
  93. return -1, fmt.Errorf("could not find field matching %v", name)
  94. }
  95. // isList returns true if the element is something we can Len()
  96. func isList(v reflect.Value) bool {
  97. switch v.Type().Kind() {
  98. case reflect.Array, reflect.Slice:
  99. return true
  100. default:
  101. return false
  102. }
  103. }
  104. // Write takes a value and copies it to the target
  105. func copy(t reflect.Value, v reflect.Value) (err error) {
  106. // if something ends up panicing we need to catch it in a deferred func
  107. defer func() {
  108. if r := recover(); r != nil {
  109. // if we paniced with an error
  110. if _, ok := r.(error); ok {
  111. // cast the result to an error object
  112. err = r.(error)
  113. } else if _, ok := r.(string); ok {
  114. // otherwise we could have paniced with a string so wrap it in an error
  115. err = errors.New(r.(string))
  116. }
  117. }
  118. }()
  119. // if we are copying from a string result to something else
  120. if v.Kind() == reflect.String && v.Type() != t.Type() {
  121. var castVal interface{}
  122. var casterr error
  123. vString := v.Interface().(string)
  124. switch t.Kind() {
  125. case reflect.Bool:
  126. castVal, casterr = strconv.ParseBool(vString)
  127. case reflect.Int:
  128. castVal, casterr = strconv.Atoi(vString)
  129. case reflect.Int8:
  130. var val64 int64
  131. val64, casterr = strconv.ParseInt(vString, 10, 8)
  132. if casterr == nil {
  133. castVal = int8(val64)
  134. }
  135. case reflect.Int16:
  136. var val64 int64
  137. val64, casterr = strconv.ParseInt(vString, 10, 16)
  138. if casterr == nil {
  139. castVal = int16(val64)
  140. }
  141. case reflect.Int32:
  142. var val64 int64
  143. val64, casterr = strconv.ParseInt(vString, 10, 32)
  144. if casterr == nil {
  145. castVal = int32(val64)
  146. }
  147. case reflect.Int64:
  148. castVal, casterr = strconv.ParseInt(vString, 10, 64)
  149. case reflect.Uint:
  150. var val64 uint64
  151. val64, casterr = strconv.ParseUint(vString, 10, 8)
  152. if casterr == nil {
  153. castVal = uint(val64)
  154. }
  155. case reflect.Uint8:
  156. var val64 uint64
  157. val64, casterr = strconv.ParseUint(vString, 10, 8)
  158. if casterr == nil {
  159. castVal = uint8(val64)
  160. }
  161. case reflect.Uint16:
  162. var val64 uint64
  163. val64, casterr = strconv.ParseUint(vString, 10, 16)
  164. if casterr == nil {
  165. castVal = uint16(val64)
  166. }
  167. case reflect.Uint32:
  168. var val64 uint64
  169. val64, casterr = strconv.ParseUint(vString, 10, 32)
  170. if casterr == nil {
  171. castVal = uint32(val64)
  172. }
  173. case reflect.Uint64:
  174. castVal, casterr = strconv.ParseUint(vString, 10, 64)
  175. case reflect.Float32:
  176. var val64 float64
  177. val64, casterr = strconv.ParseFloat(vString, 32)
  178. if casterr == nil {
  179. castVal = float32(val64)
  180. }
  181. case reflect.Float64:
  182. castVal, casterr = strconv.ParseFloat(vString, 64)
  183. default:
  184. return fmt.Errorf("Unable to convert from string to type %s", t.Kind())
  185. }
  186. if casterr != nil {
  187. return casterr
  188. }
  189. t.Set(reflect.ValueOf(castVal))
  190. return
  191. }
  192. // if we are copying from one slice or array to another
  193. if isList(v) && isList(t) {
  194. // loop over every item in the desired value
  195. for i := 0; i < v.Len(); i++ {
  196. // write to the target given its kind
  197. switch t.Kind() {
  198. // if its a slice
  199. case reflect.Slice:
  200. // an object of the correct type
  201. obj := reflect.Indirect(reflect.New(t.Type().Elem()))
  202. // write the appropriate value to the obj and catch any errors
  203. if err := copy(obj, v.Index(i)); err != nil {
  204. return err
  205. }
  206. // just append the value to the end
  207. t.Set(reflect.Append(t, obj))
  208. // otherwise it could be an array
  209. case reflect.Array:
  210. // set the index to the appropriate value
  211. copy(t.Slice(i, i+1).Index(0), v.Index(i))
  212. }
  213. }
  214. } else {
  215. // set the value to the target
  216. t.Set(v)
  217. }
  218. // we're done
  219. return
  220. }