database: add FormatDSN #277
@ -15,7 +15,6 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Params map[string]string
|
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
@ -23,6 +22,50 @@ type Config struct {
|
|||||||
Host string
|
Host string
|
||||||
Port string
|
Port string
|
||||||
Database string
|
Database string
|
||||||
|
Params []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) FormatDSN() string {
|
||||||
|
var s strings.Builder
|
||||||
|
|
||||||
|
if len(cfg.Scheme) > 0 {
|
||||||
|
s.WriteString(cfg.Scheme + "://")
|
||||||
|
}
|
||||||
|
// [username[:password]@]
|
||||||
|
if len(cfg.Username) > 0 {
|
||||||
|
s.WriteString(cfg.Username)
|
||||||
|
if len(cfg.Password) > 0 {
|
||||||
|
s.WriteByte(':')
|
||||||
|
s.WriteString(url.PathEscape(cfg.Password))
|
||||||
|
}
|
||||||
|
s.WriteByte('@')
|
||||||
|
}
|
||||||
|
|
||||||
|
// [host:port]
|
||||||
|
if len(cfg.Host) > 0 {
|
||||||
|
s.WriteString(cfg.Host)
|
||||||
|
if len(cfg.Port) > 0 {
|
||||||
|
s.WriteByte(':')
|
||||||
|
s.WriteString(cfg.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /dbname
|
||||||
|
s.WriteByte('/')
|
||||||
|
s.WriteString(url.PathEscape(cfg.Database))
|
||||||
|
|
||||||
|
for i := 0; i < len(cfg.Params); i += 2 {
|
||||||
|
if i == 0 {
|
||||||
|
s.WriteString("?")
|
||||||
|
} else {
|
||||||
|
s.WriteString("&")
|
||||||
|
}
|
||||||
|
s.WriteString(cfg.Params[i])
|
||||||
|
s.WriteString("=")
|
||||||
|
s.WriteString(cfg.Params[i+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseDSN(dsn string) (*Config, error) {
|
func ParseDSN(dsn string) (*Config, error) {
|
||||||
@ -84,13 +127,13 @@ func ParseDSN(dsn string) (*Config, error) {
|
|||||||
for j = i + 1; j < len(dsn); j++ {
|
for j = i + 1; j < len(dsn); j++ {
|
||||||
if dsn[j] == '?' {
|
if dsn[j] == '?' {
|
||||||
parts := strings.Split(dsn[j+1:], "&")
|
parts := strings.Split(dsn[j+1:], "&")
|
||||||
cfg.Params = make(map[string]string, len(parts))
|
cfg.Params = make([]string, 0, len(parts)*2)
|
||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
k, v, found := strings.Cut(p, "=")
|
k, v, found := strings.Cut(p, "=")
|
||||||
if !found {
|
if !found {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cfg.Params[k] = v
|
cfg.Params = append(cfg.Params, k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,3 +14,18 @@ func TestParseDSN(t *testing.T) {
|
|||||||
t.Fatalf("parsing error")
|
t.Fatalf("parsing error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFormatDSN(t *testing.T) {
|
||||||
|
src := "postgres://username:p@ssword#@host:12345/dbname?key1=val2&key2=val2"
|
||||||
|
cfg, err := ParseDSN(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dst, err := url.PathUnescape(cfg.FormatDSN())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if src != dst {
|
||||||
|
t.Fatalf("\n%s\n%s", src, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user