dsn.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. // Package dsn provide parse dsn and bind to struct
  2. // see http://git.bilibili.co/platform/go-common/issues/279
  3. package dsn
  4. import (
  5. "net/url"
  6. "reflect"
  7. "strings"
  8. "gopkg.in/go-playground/validator.v9"
  9. )
  10. var _validator *validator.Validate
  11. func init() {
  12. _validator = validator.New()
  13. }
  14. // DSN a DSN represents a parsed DSN as same as url.URL.
  15. type DSN struct {
  16. *url.URL
  17. }
  18. // Bind dsn to specify struct and validate use use go-playground/validator format
  19. //
  20. // The bind of each struct field can be customized by the format string
  21. // stored under the 'dsn' key in the struct field's tag. The format string
  22. // gives the name of the field, possibly followed by a comma-separated
  23. // list of options. The name may be empty in order to specify options
  24. // without overriding the default field name.
  25. //
  26. // A two type data you can bind to struct
  27. // built-in values, use below keys to bind built-in value
  28. // username
  29. // password
  30. // address
  31. // network
  32. // the value in query string, use query.{name} to bind value in query string
  33. //
  34. // As a special case, if the field tag is "-", the field is always omitted.
  35. // NOTE: that a field with name "-" can still be generated using the tag "-,".
  36. //
  37. // Examples of struct field tags and their meanings:
  38. // // Field bind username
  39. // Field string `dsn:"username"`
  40. // // Field is ignored by this package.
  41. // Field string `dsn:"-"`
  42. // // Field bind value from query
  43. // Field string `dsn:"query.name"`
  44. //
  45. func (d *DSN) Bind(v interface{}) (url.Values, error) {
  46. assignFuncs := make(map[string]assignFunc)
  47. if d.User != nil {
  48. username := d.User.Username()
  49. password, ok := d.User.Password()
  50. if ok {
  51. assignFuncs["password"] = stringsAssignFunc(password)
  52. }
  53. assignFuncs["username"] = stringsAssignFunc(username)
  54. }
  55. assignFuncs["address"] = addressesAssignFunc(d.Addresses())
  56. assignFuncs["network"] = stringsAssignFunc(d.Scheme)
  57. query, err := bindQuery(d.Query(), v, assignFuncs)
  58. if err != nil {
  59. return nil, err
  60. }
  61. return query, _validator.Struct(v)
  62. }
  63. func addressesAssignFunc(addresses []string) assignFunc {
  64. return func(v reflect.Value, to tagOpt) error {
  65. if v.Kind() == reflect.String {
  66. if addresses[0] == "" && to.Default != "" {
  67. v.SetString(to.Default)
  68. } else {
  69. v.SetString(addresses[0])
  70. }
  71. return nil
  72. }
  73. if !(v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.String) {
  74. return &BindTypeError{Value: strings.Join(addresses, ","), Type: v.Type()}
  75. }
  76. vals := reflect.MakeSlice(v.Type(), len(addresses), len(addresses))
  77. for i, address := range addresses {
  78. vals.Index(i).SetString(address)
  79. }
  80. if v.CanSet() {
  81. v.Set(vals)
  82. }
  83. return nil
  84. }
  85. }
  86. // Addresses parse host split by ','
  87. // For Unix networks, return ['path']
  88. func (d *DSN) Addresses() []string {
  89. switch d.Scheme {
  90. case "unix", "unixgram", "unixpacket":
  91. return []string{d.Path}
  92. }
  93. return strings.Split(d.Host, ",")
  94. }
  95. // Parse parses rawdsn into a URL structure.
  96. func Parse(rawdsn string) (*DSN, error) {
  97. u, err := url.Parse(rawdsn)
  98. return &DSN{URL: u}, err
  99. }