utils.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright (C) 2016 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 util
  7. import (
  8. "fmt"
  9. "net/url"
  10. "reflect"
  11. "sort"
  12. "strconv"
  13. "strings"
  14. )
  15. // SetDefaults sets default values on a struct, based on the default annotation.
  16. func SetDefaults(data interface{}) error {
  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 parser, ok := f.Interface().(interface {
  25. ParseDefault(string) (interface{}, error)
  26. }); ok {
  27. val, err := parser.ParseDefault(v)
  28. if err != nil {
  29. panic(err)
  30. }
  31. f.Set(reflect.ValueOf(val))
  32. continue
  33. }
  34. switch f.Interface().(type) {
  35. case string:
  36. f.SetString(v)
  37. case int:
  38. i, err := strconv.ParseInt(v, 10, 64)
  39. if err != nil {
  40. return err
  41. }
  42. f.SetInt(i)
  43. case float64:
  44. i, err := strconv.ParseFloat(v, 64)
  45. if err != nil {
  46. return err
  47. }
  48. f.SetFloat(i)
  49. case bool:
  50. f.SetBool(v == "true")
  51. case []string:
  52. // We don't do anything with string slices here. Any default
  53. // we set will be appended to by the XML decoder, so we fill
  54. // those after decoding.
  55. default:
  56. panic(f.Type())
  57. }
  58. }
  59. }
  60. return nil
  61. }
  62. // CopyMatchingTag copies fields tagged tag:"value" from "from" struct onto "to" struct.
  63. func CopyMatchingTag(from interface{}, to interface{}, tag string, shouldCopy func(value string) bool) {
  64. fromStruct := reflect.ValueOf(from).Elem()
  65. fromType := fromStruct.Type()
  66. toStruct := reflect.ValueOf(to).Elem()
  67. toType := toStruct.Type()
  68. if fromType != toType {
  69. panic(fmt.Sprintf("non equal types: %s != %s", fromType, toType))
  70. }
  71. for i := 0; i < toStruct.NumField(); i++ {
  72. fromField := fromStruct.Field(i)
  73. toField := toStruct.Field(i)
  74. structTag := toType.Field(i).Tag
  75. v := structTag.Get(tag)
  76. if shouldCopy(v) {
  77. toField.Set(fromField)
  78. }
  79. }
  80. }
  81. // UniqueStrings returns a list on unique strings, trimming and sorting them
  82. // at the same time.
  83. func UniqueStrings(ss []string) []string {
  84. var m = make(map[string]bool, len(ss))
  85. for _, s := range ss {
  86. m[strings.Trim(s, " ")] = true
  87. }
  88. var us = make([]string, 0, len(m))
  89. for k := range m {
  90. us = append(us, k)
  91. }
  92. sort.Strings(us)
  93. return us
  94. }
  95. // FillNilSlices sets default value on slices that are still nil.
  96. func FillNilSlices(data interface{}) error {
  97. s := reflect.ValueOf(data).Elem()
  98. t := s.Type()
  99. for i := 0; i < s.NumField(); i++ {
  100. f := s.Field(i)
  101. tag := t.Field(i).Tag
  102. v := tag.Get("default")
  103. if len(v) > 0 {
  104. switch f.Interface().(type) {
  105. case []string:
  106. if f.IsNil() {
  107. // Treat the default as a comma separated slice
  108. vs := strings.Split(v, ",")
  109. for i := range vs {
  110. vs[i] = strings.TrimSpace(vs[i])
  111. }
  112. rv := reflect.MakeSlice(reflect.TypeOf([]string{}), len(vs), len(vs))
  113. for i, v := range vs {
  114. rv.Index(i).SetString(v)
  115. }
  116. f.Set(rv)
  117. }
  118. }
  119. }
  120. }
  121. return nil
  122. }
  123. // Address constructs a URL from the given network and hostname.
  124. func Address(network, host string) string {
  125. u := url.URL{
  126. Scheme: network,
  127. Host: host,
  128. }
  129. return u.String()
  130. }