148 lines
3.9 KiB
Go
148 lines
3.9 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
mmodel "github.com/tech/sendico/fx/ingestor/internal/model"
|
|
"github.com/tech/sendico/pkg/db"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
const defaultPollInterval = 30 * time.Second
|
|
|
|
type Config struct {
|
|
PollIntervalSeconds int `yaml:"poll_interval_seconds"`
|
|
Market MarketConfig `yaml:"market"`
|
|
Database *db.Config `yaml:"database"`
|
|
Metrics *MetricsConfig `yaml:"metrics"`
|
|
|
|
pairs []Pair
|
|
pairsBySource map[mmodel.Driver][]PairConfig
|
|
}
|
|
|
|
func Load(path string) (*Config, error) {
|
|
if path == "" {
|
|
return nil, merrors.InvalidArgument("config: path is empty")
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, merrors.InternalWrap(err, "config: failed to read file")
|
|
}
|
|
|
|
cfg := &Config{}
|
|
if err := yaml.Unmarshal(data, cfg); err != nil {
|
|
return nil, merrors.InternalWrap(err, "config: failed to parse yaml")
|
|
}
|
|
|
|
if len(cfg.Market.Sources) == 0 {
|
|
return nil, merrors.InvalidArgument("config: no market sources configured")
|
|
}
|
|
sourceSet := make(map[mmodel.Driver]struct{}, len(cfg.Market.Sources))
|
|
for idx := range cfg.Market.Sources {
|
|
src := &cfg.Market.Sources[idx]
|
|
if src.Driver.IsEmpty() {
|
|
return nil, merrors.InvalidArgument("config: market source driver is empty")
|
|
}
|
|
sourceSet[src.Driver] = struct{}{}
|
|
}
|
|
|
|
if len(cfg.Market.Pairs) == 0 {
|
|
return nil, merrors.InvalidArgument("config: no pairs configured")
|
|
}
|
|
|
|
normalizedPairs := make(map[string][]PairConfig, len(cfg.Market.Pairs))
|
|
pairsBySource := make(map[mmodel.Driver][]PairConfig, len(cfg.Market.Pairs))
|
|
var flattened []Pair
|
|
|
|
for rawSource, pairList := range cfg.Market.Pairs {
|
|
driver := mmodel.Driver(rawSource)
|
|
if driver.IsEmpty() {
|
|
return nil, merrors.InvalidArgument("config: pair source is empty")
|
|
}
|
|
if _, ok := sourceSet[driver]; !ok {
|
|
return nil, merrors.InvalidArgument("config: pair references unknown source: "+driver.String(), "pairs."+driver.String())
|
|
}
|
|
|
|
processed := make([]PairConfig, len(pairList))
|
|
for idx := range pairList {
|
|
pair := pairList[idx]
|
|
pair.Base = strings.ToUpper(strings.TrimSpace(pair.Base))
|
|
pair.Quote = strings.ToUpper(strings.TrimSpace(pair.Quote))
|
|
pair.Symbol = strings.TrimSpace(pair.Symbol)
|
|
if pair.Base == "" || pair.Quote == "" || pair.Symbol == "" {
|
|
return nil, merrors.InvalidArgument("config: pair entries must define base, quote, and symbol", "pairs."+driver.String())
|
|
}
|
|
if strings.TrimSpace(pair.Provider) == "" {
|
|
pair.Provider = strings.ToLower(driver.String())
|
|
}
|
|
processed[idx] = pair
|
|
flattened = append(flattened, Pair{
|
|
PairConfig: pair,
|
|
Source: driver,
|
|
})
|
|
}
|
|
pairsBySource[driver] = processed
|
|
normalizedPairs[driver.String()] = processed
|
|
}
|
|
|
|
cfg.Market.Pairs = normalizedPairs
|
|
cfg.pairsBySource = pairsBySource
|
|
cfg.pairs = flattened
|
|
if cfg.Database == nil {
|
|
return nil, merrors.InvalidArgument("config: database configuration is required")
|
|
}
|
|
|
|
if cfg.Metrics != nil && cfg.Metrics.Enabled {
|
|
cfg.Metrics.Address = strings.TrimSpace(cfg.Metrics.Address)
|
|
if cfg.Metrics.Address == "" {
|
|
cfg.Metrics.Address = ":9102"
|
|
}
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func (c *Config) PollInterval() time.Duration {
|
|
if c == nil {
|
|
return defaultPollInterval
|
|
}
|
|
if c.PollIntervalSeconds <= 0 {
|
|
return defaultPollInterval
|
|
}
|
|
return time.Duration(c.PollIntervalSeconds) * time.Second
|
|
}
|
|
|
|
func (c *Config) Pairs() []Pair {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
out := make([]Pair, len(c.pairs))
|
|
copy(out, c.pairs)
|
|
return out
|
|
}
|
|
|
|
func (c *Config) PairsBySource() map[mmodel.Driver][]PairConfig {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
out := make(map[mmodel.Driver][]PairConfig, len(c.pairsBySource))
|
|
for driver, pairs := range c.pairsBySource {
|
|
cp := make([]PairConfig, len(pairs))
|
|
copy(cp, pairs)
|
|
out[driver] = cp
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (c *Config) MetricsConfig() *MetricsConfig {
|
|
if c == nil || c.Metrics == nil {
|
|
return nil
|
|
}
|
|
cp := *c.Metrics
|
|
return &cp
|
|
}
|