package components import ( "context" "fmt" "io" "time" "git.loyso.art/frx/eway/internal/config" "git.loyso.art/frx/eway/internal/interconnect/eway" "git.loyso.art/frx/eway/internal/storage" xbadger "git.loyso.art/frx/eway/internal/storage/badger" "github.com/BurntSushi/toml" "github.com/dgraph-io/badger/v4" "github.com/rs/zerolog" "github.com/samber/do" ) // Yeah, singleton is not good UNLESS you're really lazy var diInjector *do.Injector func GetEwayClient() (eway.Client, error) { return do.Invoke[eway.Client](diInjector) } func GetRepository() (storage.Repository, error) { adapter, err := do.Invoke[*storageRepositoryAdapter](diInjector) if err != nil { return nil, err } return adapter.entity, nil } func GetLogger() (zerolog.Logger, error) { return do.Invoke[zerolog.Logger](diInjector) } func SetupDI(ctx context.Context, cfgpath string) error { cfg, err := parseSettings(cfgpath) if err != nil { return err } diInjector = do.New() do.Provide(diInjector, func(i *do.Injector) (zerolog.Logger, error) { tsSet := func(wr *zerolog.ConsoleWriter) { wr.TimeFormat = time.RFC3339 } log := zerolog.New(zerolog.NewConsoleWriter(tsSet)).With().Timestamp().Str("app", "converter").Logger() return log, nil }) do.Provide[eway.Client](diInjector, func(i *do.Injector) (eway.Client, error) { log, err := GetLogger() if err != nil { return nil, fmt.Errorf("getting logger: %w", err) } client, err := eway.New(eway.Config(cfg.Eway), log) if err != nil { return nil, fmt.Errorf("making new eway client: %w", err) } return client, nil }) do.Provide[*badgerDBAdapter](diInjector, func(i *do.Injector) (*badgerDBAdapter, error) { db, err := xbadger.Open(ctx, cfg.Badger.Dir, cfg.Badger.Debug, zerolog.Nop()) if err != nil { return nil, fmt.Errorf("getting db: %w", err) } out := &badgerDBAdapter{entity: db} return out, nil }) do.Provide[*storageRepositoryAdapter](diInjector, func(i *do.Injector) (*storageRepositoryAdapter, error) { db, err := getDB() if err != nil { return nil, err } client, err := xbadger.NewClient(db) if err != nil { return nil, fmt.Errorf("getting badger client: %w", err) } out := &storageRepositoryAdapter{entity: client} return out, nil }) return nil } func Shutdown() error { return diInjector.Shutdown() } func getDB() (*badger.DB, error) { adapter, err := do.Invoke[*badgerDBAdapter](diInjector) if err != nil { return nil, err } return adapter.entity, nil } type settings struct { Badger config.Badger `toml:"badger"` Log config.Log `toml:"log"` Eway config.Eway `toml:"eway"` } func parseSettings(cfgpath string) (cfg settings, err error) { _, err = toml.DecodeFile(cfgpath, &cfg) if err != nil { return cfg, fmt.Errorf("parsing file: %w", err) } return cfg, nil } type entityCloserAdapter[T io.Closer] struct { entity T } func (a entityCloserAdapter[T]) Shutdown() error { return a.entity.Close() } type storageRepositoryAdapter entityCloserAdapter[storage.Repository] type badgerDBAdapter entityCloserAdapter[*badger.DB]