138 lines
3.5 KiB
Go
138 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
|
|
// mysql driver.
|
|
_ "github.com/go-sql-driver/mysql"
|
|
|
|
// psql driver.
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
type Interface struct {
|
|
str interface{}
|
|
}
|
|
|
|
type Config struct {
|
|
ConfigFile string
|
|
EnvPrefix string
|
|
DSN string
|
|
}
|
|
|
|
// Simple constructor.
|
|
func New(str interface{}) Interface {
|
|
return Interface{str: str}
|
|
}
|
|
|
|
// Method wraps discrete methods.
|
|
func (s Interface) Combine(config Config) error {
|
|
if config.ConfigFile != "" {
|
|
fmt.Printf("try to apply config from file %s...\n", config.ConfigFile)
|
|
if err := s.SetFromFile(config.ConfigFile); err != nil {
|
|
return fmt.Errorf("can't apply config from file: %w", err)
|
|
}
|
|
}
|
|
if config.EnvPrefix != "" {
|
|
fmt.Printf("try to apply config from environment...\n")
|
|
if err := s.SetFromEnv(config.EnvPrefix); err != nil {
|
|
return fmt.Errorf("can't apply envvars to config:%w", err)
|
|
}
|
|
}
|
|
if config.DSN != "" {
|
|
fmt.Printf("try to apply config from DSN %s...\n", config.DSN)
|
|
db, dbname, err := DialDSN(config.DSN)
|
|
if err != nil {
|
|
return fmt.Errorf("can't dial DB:%w", err)
|
|
}
|
|
if err := s.SetFromDB(db, dbname); err != nil {
|
|
return fmt.Errorf("can't apply db lines to config:%w", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Method adds and replace config fields from file.
|
|
func (s Interface) SetFromFile(fileName string) error {
|
|
f, err := os.Open(fileName)
|
|
if err != nil {
|
|
return fmt.Errorf("can't open config file: %w", err)
|
|
}
|
|
defer f.Close()
|
|
l, err := ioutil.ReadAll(f)
|
|
if err != nil {
|
|
return fmt.Errorf("can't read content of the config file : %w", err)
|
|
}
|
|
_, err = toml.Decode(string(l), s.str)
|
|
if err != nil {
|
|
return fmt.Errorf("can't parce config file : %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Method adds and replace config fields from env.
|
|
func (s Interface) SetFromEnv(prefix string) error {
|
|
return getEnvVar(reflect.ValueOf(s.str), reflect.TypeOf(s.str), -1, prefix)
|
|
}
|
|
|
|
func DialDSN(dsn string) (db *sql.DB, dbname string, err error) {
|
|
m := strings.FieldsFunc(dsn, func(r rune) bool { return r == ':' || r == '@' || r == '/' })
|
|
dbName := m[len(m)-1]
|
|
if dbName == "" {
|
|
return nil, "", fmt.Errorf("DSN not contains database name: %s", dsn)
|
|
}
|
|
|
|
var driver string
|
|
switch {
|
|
case strings.HasPrefix(dsn, "postgres://"):
|
|
driver = "postgres"
|
|
dsn = strings.TrimLeft(dsn, "postgres://")
|
|
case strings.HasPrefix(dsn, "mysql://"):
|
|
driver = "mysql"
|
|
dsn = strings.TrimLeft(dsn, "mysql://")
|
|
default:
|
|
return nil, "", errors.New("can't use unknown SQL dialect")
|
|
}
|
|
|
|
db, err = sql.Open(driver, dsn)
|
|
if err != nil {
|
|
return nil, "", fmt.Errorf("can't connect to DB: %w", err)
|
|
}
|
|
return db, dbName, nil
|
|
}
|
|
|
|
// Method adds and replace config fields from db.
|
|
func (s Interface) SetFromDB(database *sql.DB, _ string) error {
|
|
defer database.Close()
|
|
res := make(map[string]string)
|
|
var key, val string
|
|
|
|
// TODO: Перенести это в параметры.
|
|
table := "config"
|
|
q := fmt.Sprintf("SELECT %s.key, %s.value FROM %s", table, table, table)
|
|
results, err := database.Query(q)
|
|
if err != nil || results.Err() != nil {
|
|
return fmt.Errorf("can't get key-value pairs from DB: %w", err)
|
|
}
|
|
defer results.Close()
|
|
for results.Next() {
|
|
err = results.Scan(&key, &val)
|
|
if err != nil {
|
|
return fmt.Errorf("can't parse key-value into vars: %w", err)
|
|
}
|
|
res[strings.ToLower(key)] = val
|
|
}
|
|
if err = parseToStruct(reflect.ValueOf(s.str), reflect.TypeOf(s.str), -1, "", res); err != nil {
|
|
return fmt.Errorf("can't parse into struct: %w", err)
|
|
}
|
|
return nil
|
|
}
|