structutil.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright (C) 2023 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package structutil
  7. import (
  8. "reflect"
  9. "strconv"
  10. "strings"
  11. )
  12. type defaultParser interface {
  13. ParseDefault(string) error
  14. }
  15. // SetDefaults sets default values on a struct, based on the default annotation.
  16. func SetDefaults(data any) {
  17. s := reflect.ValueOf(data).Elem()
  18. t := s.Type()
  19. for i := 0; i < s.NumField(); i++ {
  20. f := s.Field(i)
  21. tag := t.Field(i).Tag
  22. v := tag.Get("default")
  23. if len(v) > 0 {
  24. if f.CanInterface() {
  25. if parser, ok := f.Interface().(defaultParser); ok {
  26. if err := parser.ParseDefault(v); err != nil {
  27. panic(err)
  28. }
  29. continue
  30. }
  31. }
  32. if f.CanAddr() && f.Addr().CanInterface() {
  33. if parser, ok := f.Addr().Interface().(defaultParser); ok {
  34. if err := parser.ParseDefault(v); err != nil {
  35. panic(err)
  36. }
  37. continue
  38. }
  39. }
  40. switch f.Interface().(type) {
  41. case string:
  42. f.SetString(v)
  43. case int, uint32, int32, int64, uint64:
  44. i, err := strconv.ParseInt(v, 10, 64)
  45. if err != nil {
  46. panic(err)
  47. }
  48. f.SetInt(i)
  49. case float64, float32:
  50. i, err := strconv.ParseFloat(v, 64)
  51. if err != nil {
  52. panic(err)
  53. }
  54. f.SetFloat(i)
  55. case bool:
  56. f.SetBool(v == "true")
  57. case []string:
  58. // We don't do anything with string slices here. Any default
  59. // we set will be appended to by the XML decoder, so we fill
  60. // those after decoding.
  61. default:
  62. panic(f.Type())
  63. }
  64. } else if f.CanSet() && f.Kind() == reflect.Struct && f.CanAddr() {
  65. if addr := f.Addr(); addr.CanInterface() {
  66. SetDefaults(addr.Interface())
  67. }
  68. }
  69. }
  70. }
  71. func FillNilExceptDeprecated(data any) {
  72. fillNil(data, true)
  73. }
  74. func FillNil(data any) {
  75. fillNil(data, false)
  76. }
  77. func fillNil(data any, skipDeprecated bool) {
  78. s := reflect.ValueOf(data).Elem()
  79. t := s.Type()
  80. for i := 0; i < s.NumField(); i++ {
  81. if skipDeprecated && strings.HasPrefix(t.Field(i).Name, "Deprecated") {
  82. continue
  83. }
  84. f := s.Field(i)
  85. for f.Kind() == reflect.Ptr && f.IsZero() && f.CanSet() {
  86. newValue := reflect.New(f.Type().Elem())
  87. f.Set(newValue)
  88. f = f.Elem()
  89. }
  90. if f.CanSet() {
  91. if f.IsZero() {
  92. switch f.Kind() {
  93. case reflect.Map:
  94. f.Set(reflect.MakeMap(f.Type()))
  95. case reflect.Slice:
  96. f.Set(reflect.MakeSlice(f.Type(), 0, 0))
  97. case reflect.Chan:
  98. f.Set(reflect.MakeChan(f.Type(), 0))
  99. }
  100. }
  101. switch f.Kind() {
  102. case reflect.Slice:
  103. if f.Type().Elem().Kind() != reflect.Struct {
  104. continue
  105. }
  106. for i := 0; i < f.Len(); i++ {
  107. fillNil(f.Index(i).Addr().Interface(), skipDeprecated)
  108. }
  109. case reflect.Struct:
  110. if f.CanAddr() {
  111. if addr := f.Addr(); addr.CanInterface() {
  112. fillNil(addr.Interface(), skipDeprecated)
  113. }
  114. }
  115. }
  116. }
  117. }
  118. }
  119. // FillNilSlices sets default value on slices that are still nil.
  120. func FillNilSlices(data any) error {
  121. s := reflect.ValueOf(data).Elem()
  122. t := s.Type()
  123. for i := 0; i < s.NumField(); i++ {
  124. f := s.Field(i)
  125. tag := t.Field(i).Tag
  126. v := tag.Get("default")
  127. if len(v) > 0 {
  128. switch f.Interface().(type) {
  129. case []string:
  130. if f.IsNil() {
  131. // Treat the default as a comma separated slice
  132. vs := strings.Split(v, ",")
  133. for i := range vs {
  134. vs[i] = strings.TrimSpace(vs[i])
  135. }
  136. rv := reflect.MakeSlice(reflect.TypeOf([]string{}), len(vs), len(vs))
  137. for i, v := range vs {
  138. rv.Index(i).SetString(v)
  139. }
  140. f.Set(rv)
  141. }
  142. }
  143. }
  144. }
  145. return nil
  146. }