attrs.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Copyright (c) 2021 Tulir Asokan
  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
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. package binary
  7. import (
  8. "fmt"
  9. "strconv"
  10. "time"
  11. "git.bobomao.top/joey/testwh/types"
  12. )
  13. // AttrUtility is a helper struct for reading multiple XML attributes and checking for errors afterwards.
  14. //
  15. // The functions return values directly and append any decoding errors to the Errors slice. The
  16. // slice can then be checked after all necessary attributes are read, instead of having to check
  17. // each attribute for errors separately.
  18. type AttrUtility struct {
  19. Attrs Attrs
  20. Errors []error
  21. }
  22. // AttrGetter returns the AttrUtility for this Node.
  23. func (n *Node) AttrGetter() *AttrUtility {
  24. return &AttrUtility{Attrs: n.Attrs, Errors: make([]error, 0)}
  25. }
  26. func (au *AttrUtility) GetJID(key string, require bool) (jidVal types.JID, ok bool) {
  27. var val interface{}
  28. if val, ok = au.Attrs[key]; !ok {
  29. if require {
  30. au.Errors = append(au.Errors, fmt.Errorf("didn't find required JID attribute '%s'", key))
  31. }
  32. } else if jidVal, ok = val.(types.JID); !ok {
  33. au.Errors = append(au.Errors, fmt.Errorf("expected attribute '%s' to be JID, but was %T", key, val))
  34. }
  35. return
  36. }
  37. // OptionalJID returns the JID under the given key. If there's no valid JID under the given key, this will return nil.
  38. // However, if the attribute is completely missing, this will not store an error.
  39. func (au *AttrUtility) OptionalJID(key string) *types.JID {
  40. jid, ok := au.GetJID(key, false)
  41. if ok {
  42. return &jid
  43. }
  44. return nil
  45. }
  46. // OptionalJIDOrEmpty returns the JID under the given key. If there's no valid JID under the given key, this will return an empty JID.
  47. // However, if the attribute is completely missing, this will not store an error.
  48. func (au *AttrUtility) OptionalJIDOrEmpty(key string) types.JID {
  49. jid, ok := au.GetJID(key, false)
  50. if ok {
  51. return jid
  52. }
  53. return types.EmptyJID
  54. }
  55. // JID returns the JID under the given key.
  56. // If there's no valid JID under the given key, an error will be stored and a blank JID struct will be returned.
  57. func (au *AttrUtility) JID(key string) types.JID {
  58. jid, _ := au.GetJID(key, true)
  59. return jid
  60. }
  61. func (au *AttrUtility) GetString(key string, require bool) (strVal string, ok bool) {
  62. var val interface{}
  63. if val, ok = au.Attrs[key]; !ok {
  64. if require {
  65. au.Errors = append(au.Errors, fmt.Errorf("didn't find required attribute '%s'", key))
  66. }
  67. } else if strVal, ok = val.(string); !ok {
  68. au.Errors = append(au.Errors, fmt.Errorf("expected attribute '%s' to be string, but was %T", key, val))
  69. }
  70. return
  71. }
  72. func (au *AttrUtility) GetInt64(key string, require bool) (int64, bool) {
  73. if strVal, ok := au.GetString(key, require); !ok {
  74. return 0, false
  75. } else if intVal, err := strconv.ParseInt(strVal, 10, 64); err != nil {
  76. au.Errors = append(au.Errors, fmt.Errorf("failed to parse int in attribute '%s': %w", key, err))
  77. return 0, false
  78. } else {
  79. return intVal, true
  80. }
  81. }
  82. func (au *AttrUtility) GetUint64(key string, require bool) (uint64, bool) {
  83. if strVal, ok := au.GetString(key, require); !ok {
  84. return 0, false
  85. } else if intVal, err := strconv.ParseUint(strVal, 10, 64); err != nil {
  86. au.Errors = append(au.Errors, fmt.Errorf("failed to parse uint in attribute '%s': %w", key, err))
  87. return 0, false
  88. } else {
  89. return intVal, true
  90. }
  91. }
  92. func (au *AttrUtility) GetBool(key string, require bool) (bool, bool) {
  93. if strVal, ok := au.GetString(key, require); !ok {
  94. return false, false
  95. } else if boolVal, err := strconv.ParseBool(strVal); err != nil {
  96. au.Errors = append(au.Errors, fmt.Errorf("failed to parse bool in attribute '%s': %w", key, err))
  97. return false, false
  98. } else {
  99. return boolVal, true
  100. }
  101. }
  102. func (au *AttrUtility) GetUnixTime(key string, require bool) (time.Time, bool) {
  103. if intVal, ok := au.GetInt64(key, require); !ok {
  104. return time.Time{}, false
  105. } else if intVal == 0 {
  106. return time.Time{}, true
  107. } else {
  108. return time.Unix(intVal, 0), true
  109. }
  110. }
  111. func (au *AttrUtility) GetUnixMilli(key string, require bool) (time.Time, bool) {
  112. if intVal, ok := au.GetInt64(key, require); !ok {
  113. return time.Time{}, false
  114. } else if intVal == 0 {
  115. return time.Time{}, true
  116. } else {
  117. return time.UnixMilli(intVal), true
  118. }
  119. }
  120. // OptionalString returns the string under the given key.
  121. func (au *AttrUtility) OptionalString(key string) string {
  122. strVal, _ := au.GetString(key, false)
  123. return strVal
  124. }
  125. // String returns the string under the given key.
  126. // If there's no valid string under the given key, an error will be stored and an empty string will be returned.
  127. func (au *AttrUtility) String(key string) string {
  128. strVal, _ := au.GetString(key, true)
  129. return strVal
  130. }
  131. func (au *AttrUtility) OptionalInt(key string) int {
  132. val, _ := au.GetInt64(key, false)
  133. return int(val)
  134. }
  135. func (au *AttrUtility) Int(key string) int {
  136. val, _ := au.GetInt64(key, true)
  137. return int(val)
  138. }
  139. func (au *AttrUtility) Int64(key string) int64 {
  140. val, _ := au.GetInt64(key, true)
  141. return val
  142. }
  143. func (au *AttrUtility) Uint64(key string) uint64 {
  144. val, _ := au.GetUint64(key, true)
  145. return val
  146. }
  147. func (au *AttrUtility) OptionalBool(key string) bool {
  148. val, _ := au.GetBool(key, false)
  149. return val
  150. }
  151. func (au *AttrUtility) Bool(key string) bool {
  152. val, _ := au.GetBool(key, true)
  153. return val
  154. }
  155. func (au *AttrUtility) OptionalUnixTime(key string) time.Time {
  156. val, _ := au.GetUnixTime(key, false)
  157. return val
  158. }
  159. func (au *AttrUtility) UnixTime(key string) time.Time {
  160. val, _ := au.GetUnixTime(key, true)
  161. return val
  162. }
  163. func (au *AttrUtility) OptionalUnixMilli(key string) time.Time {
  164. val, _ := au.GetUnixMilli(key, false)
  165. return val
  166. }
  167. func (au *AttrUtility) UnixMilli(key string) time.Time {
  168. val, _ := au.GetUnixMilli(key, true)
  169. return val
  170. }
  171. // OK returns true if there are no errors.
  172. func (au *AttrUtility) OK() bool {
  173. return len(au.Errors) == 0
  174. }
  175. // Error returns the list of errors as a single error interface, or nil if there are no errors.
  176. func (au *AttrUtility) Error() error {
  177. if au.OK() {
  178. return nil
  179. }
  180. return ErrorList(au.Errors)
  181. }
  182. // ErrorList is a list of errors that implements the error interface itself.
  183. type ErrorList []error
  184. // Error returns all the errors in the list as a string.
  185. func (el ErrorList) Error() string {
  186. return fmt.Sprintf("%+v", []error(el))
  187. }