glom.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package glom
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strconv"
  6. "strings"
  7. "github.com/fatih/structs"
  8. )
  9. // Based on sliceToInterface
  10. func mapToInterface(data interface{}) (map[string]interface{}, error) {
  11. mapV := reflect.ValueOf(data)
  12. if mapV.Kind() != reflect.Map {
  13. return nil, fmt.Errorf("Failed to convert %v, given %v type to map[string]interface{}", mapV, reflect.TypeOf(data))
  14. }
  15. if mapV.IsNil() || !mapV.IsValid() {
  16. return nil, fmt.Errorf("Given nil or empty map!")
  17. }
  18. result := make(map[string]interface{})
  19. keys := mapV.MapKeys()
  20. for k := range keys {
  21. //fmt.Printf("%d/%d = %v", k, len(keys), mapV.MapIndex(keys[k]))
  22. result[keys[k].String()] = mapV.MapIndex(keys[k]).Interface()
  23. }
  24. return result, nil
  25. }
  26. // https://gist.github.com/heri16/077282d46ae95d48d430a90fb6accdff
  27. // I only need the length
  28. func sliceToInterface(data interface{}) ([]interface{}, error) {
  29. sliceV := reflect.ValueOf(data)
  30. if sliceV.Kind() == reflect.Slice { // Prevent us from converting an interface to interface
  31. switch data.(type) {
  32. case []interface{}:
  33. return data.([]interface{}), nil
  34. }
  35. }
  36. if sliceV.Kind() != reflect.Slice && sliceV.Kind() != reflect.Array {
  37. return nil, fmt.Errorf("Failed to convert %v, given %v type to []interface{}", sliceV, reflect.TypeOf(data))
  38. }
  39. if sliceV.IsNil() || !sliceV.IsValid() {
  40. return nil, fmt.Errorf("Given nil or empty slice!")
  41. }
  42. length := sliceV.Len()
  43. result := make([]interface{}, length)
  44. for i := 0; i < length; i++ {
  45. //fmt.Printf("%d/%d = %v\r\n", i, length-1, sliceV.Index(i))
  46. result[i] = sliceV.Index(i).Interface()
  47. }
  48. return result, nil
  49. }
  50. func getPossible(data interface{}) []string {
  51. var result []string
  52. //fmt.Printf("%v (%v)\r\n", reflect.TypeOf(data).Kind(), reflect.TypeOf(data))
  53. //fmt.Println(data)
  54. switch reflect.TypeOf(data).Kind() {
  55. case reflect.Map:
  56. mapV := reflect.ValueOf(data)
  57. keysV := mapV.MapKeys()
  58. for key := range keysV {
  59. result = append(result, keysV[key].String())
  60. }
  61. case reflect.Array, reflect.Slice:
  62. sliceV := reflect.ValueOf(data)
  63. for idx := 0; idx < sliceV.Len(); idx++ {
  64. result = append(result, fmt.Sprintf("%d", idx))
  65. }
  66. case reflect.Struct:
  67. result = structs.Names(data)
  68. }
  69. return result
  70. }
  71. func inside(possible []string, target string) bool {
  72. for _, val := range possible {
  73. if target == val {
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. func next_level(current_level interface{}, go_to string) (interface{}, error) {
  80. if inside(getPossible(current_level), go_to) {
  81. //fmt.Printf("%v (%v)\r\n", reflect.TypeOf(current_level).Kind(), reflect.TypeOf(current_level))
  82. switch reflect.TypeOf(current_level).Kind() {
  83. case reflect.Map:
  84. CL, err := mapToInterface(current_level)
  85. if err != nil {
  86. return nil, err
  87. }
  88. return CL[go_to], nil
  89. case reflect.Array, reflect.Slice:
  90. val, err := strconv.Atoi(go_to)
  91. if err == nil {
  92. CL, err := sliceToInterface(current_level)
  93. if err != nil {
  94. return nil, err
  95. }
  96. return CL[val], nil
  97. } else {
  98. return nil, err
  99. }
  100. case reflect.Struct:
  101. structV := reflect.ValueOf(current_level)
  102. return structV.FieldByName(go_to).Interface(), nil
  103. }
  104. }
  105. return nil, fmt.Errorf("Failed moving to '%s' from '%s' (%v)", go_to, current_level, reflect.TypeOf(current_level))
  106. }
  107. func list_possible(possible []string) []string {
  108. var result []string
  109. for _, val := range possible {
  110. result = append(result, fmt.Sprintf("'%s'", val))
  111. }
  112. return result
  113. }
  114. func Glom(data interface{}, path string) (interface{}, error) {
  115. complete_path := strings.Split(path, ".")
  116. //fmt.Printf("Seeking '%s' will take %d steps\r\n", path, len(complete_path))
  117. var path_taken []string
  118. var currently interface{}
  119. currently = data
  120. for _, hop := range complete_path {
  121. //fmt.Printf("current: %v\r\n", currently)
  122. //fmt.Printf("Path: '%v'\r\n", strings.Join(path_taken, "."))
  123. if hop != "*" && !inside(getPossible(currently), hop) {
  124. return nil, fmt.Errorf("Failed moving to '%s' from path of '%s', options are %s (%d)", hop, strings.Join(path_taken, "."), strings.Join(list_possible(getPossible(currently)), ", "), len(getPossible(currently)))
  125. } else {
  126. if hop != "*" {
  127. next, err := next_level(currently, hop)
  128. if err != nil {
  129. //return nil, fmt.Errorf("Failed moving to '%s' from path of '%s', options are %s (%d)", hop, strings.Join(path_taken, "."), strings.Join(list_possible(getPossible(next)), ", "), len(getPossible(next)))
  130. return nil, err
  131. } else {
  132. path_taken = append(path_taken, hop)
  133. currently = next
  134. }
  135. } else {
  136. return currently, nil
  137. }
  138. }
  139. }
  140. return currently, nil
  141. }