Skip to content

Commit

Permalink
typed config
Browse files Browse the repository at this point in the history
  • Loading branch information
rraymondgh committed Jan 24, 2025
1 parent bbb1977 commit 35df2c8
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 84 deletions.
8 changes: 8 additions & 0 deletions internal/boilerplate/config/configresolver/coerce.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ import (
"strconv"
"strings"
"time"

"github.com/bitmagnet-io/bitmagnet/internal/database/search"
)

var durationType = reflect.TypeOf(time.Duration(0))
var orderByType = reflect.TypeOf(search.TorrentContentOrderByRelevance)
var orderDirectionType = reflect.TypeOf(search.OrderDirectionDescending)

func coerceStringValue(stringValue string, valueType reflect.Type) (interface{}, error) {
// todo Fill this out
switch valueType {
case durationType:
return time.ParseDuration(stringValue)
case orderByType:
return search.ParseTorrentContentOrderBy(stringValue)
case orderDirectionType:
return search.ParseOrderDirection(stringValue)
}
switch valueType.Kind() {
case reflect.String:
Expand Down
105 changes: 42 additions & 63 deletions internal/torznab/config.go
Original file line number Diff line number Diff line change
@@ -1,87 +1,66 @@
package torznab

import (
"encoding/json"
"reflect"
"strings"

"github.com/bitmagnet-io/bitmagnet/internal/boilerplate/lazy"
"github.com/bitmagnet-io/bitmagnet/internal/database/search"
"go.uber.org/fx"
"go.uber.org/zap"
)

type Profile struct {
Name string
OrderBy search.TorrentContentOrderBy
OrderDirection search.OrderDirection
Tags []string
}

type Config struct {
Hostname *string
Profiles map[string]Profile
// Profiles []Profile // config framework does not support stuct,slice,struct,item+
// Profiles map[string]Profile // config framework does not support struct,map[string],struct,item+
DefaultProfile Profile
Profile0 Profile
Profile1 Profile
Profile2 Profile
Profile3 Profile
Profile4 Profile
Hostname string
}

type UntypedConfig struct {
Hostname *string
Profiles map[string]map[string]string
}

func NewDefaultUntypedConfig() UntypedConfig {
return UntypedConfig{}
func defaultProfile(name string) Profile {
return Profile{
Name: name,
OrderBy: search.TorrentContentOrderByRelevance,
OrderDirection: search.OrderDirectionDescending,
}
}

type Params struct {
fx.In
UntypedConfig UntypedConfig
Log *zap.SugaredLogger
}
func NewDefaultConfig() Config {
config := Config{}
// create a default profile for each Profile defined in Config struct
configValue := reflect.ValueOf(config)
for i := 0; i < configValue.Type().NumField(); i++ {
field := configValue.Type().Field(i)
if field.Type == reflect.ValueOf(Profile{}).Type() {
reflect.ValueOf(&config).Elem().FieldByName(field.Name).Set(
reflect.ValueOf(defaultProfile(strings.ToLower(field.Name))),
)
}
}
return config

type Result struct {
fx.Out
Profiles lazy.Lazy[*Config]
}

// lazy validation of strongly typed config
func New(p Params) Result {
return Result{
Profiles: lazy.New[*Config](func() (*Config, error) {
// make sure default profile has been defined.
_, ok := p.UntypedConfig.Profiles[ProfileDefault]
if !ok {
p.UntypedConfig.Profiles[ProfileDefault] = make(map[string]string, 0)
}
config := Config{
Hostname: p.UntypedConfig.Hostname,
Profiles: make(map[string]Profile, len(p.UntypedConfig.Profiles)),
}
for name, profile := range p.UntypedConfig.Profiles {
orderbyRaw, ok := profile[ProfileItemOrderBy]
if !ok {
orderbyRaw = string(search.TorrentContentOrderByRelevance)
}
orderby, err := search.ParseTorrentContentOrderBy(orderbyRaw)
if err != nil {
return nil, err
}
dirRaw, ok := profile[ProfileItemOrderDirection]
if !ok {
dirRaw = string(search.OrderDirectionDescending)
}
dir, err := search.ParseOrderDirection(dirRaw)
if err != nil {
return nil, err
}
tags := make([]string, 0)
csvTags, ok := profile[ProfileItemTags]
if ok && len(csvTags) > 0 {
tags = strings.Split(csvTags, ",")
}
config.Profiles[name] = Profile{OrderBy: orderby, OrderDirection: dir, Tags: tags}

}
log, _ := json.MarshalIndent(config, "", " ")
p.Log.Infof("torznab profiles:\n%s\n", log)
return &config, nil
}),
func (configPtr *Config) Map() map[string]Profile {
configValue := reflect.ValueOf(*configPtr)
// this will create an allocation larger than required
configMap := make(map[string]Profile, configValue.Type().NumField())
for i := 0; i < configValue.Type().NumField(); i++ {
field := configValue.Type().Field(i)
if field.Type == reflect.ValueOf(Profile{}).Type() {
profile := reflect.ValueOf(configPtr).Elem().Field(i).Interface().(Profile)
configMap[profile.Name] = profile
}
}

return configMap
}
20 changes: 8 additions & 12 deletions internal/torznab/httpserver/httpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
type Params struct {
fx.In
Client lazy.Lazy[torznab.Client]
Config lazy.Lazy[*torznab.Config]
Config torznab.Config
}

type Result struct {
Expand All @@ -36,7 +36,7 @@ func New(p Params) Result {

type builder struct {
client lazy.Lazy[torznab.Client]
config lazy.Lazy[*torznab.Config]
config torznab.Config
}

func (builder) Key() string {
Expand All @@ -46,7 +46,7 @@ func (builder) Key() string {
type torznabworker struct {
client torznab.Client
profile torznab.Profile
hostname *string
hostname string
}

func (w torznabworker) writeInternalError(c *gin.Context, err error) {
Expand Down Expand Up @@ -75,8 +75,8 @@ func (w torznabworker) writeErr(c *gin.Context, err error) {
}

func (w torznabworker) permaLinkBase(c *gin.Context) string {
if w.hostname != nil {
return *w.hostname
if w.hostname != "" {
return w.hostname
}
scheme := "http"
if c.Request.TLS != nil {
Expand Down Expand Up @@ -196,16 +196,12 @@ func (b builder) Apply(e *gin.Engine) error {
if err != nil {
return err
}
config, err := b.config.Get()
if err != nil {
return err
}
worker := torznabworker{
client: client,
hostname: config.Hostname,
hostname: b.config.Hostname,
}

e.GET("/torznab/api/*any", worker.getDefault(config.Profiles[torznab.ProfileDefault]))
e.GET("/torznab/:profile/*any", worker.getWithProfile(config.Profiles))
e.GET("/torznab/api/*any", worker.getDefault(b.config.DefaultProfile))
e.GET("/torznab/:profile/*any", worker.getWithProfile(b.config.Map()))
return nil
}
7 changes: 0 additions & 7 deletions internal/torznab/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,3 @@ const (
ParamLimit = "limit"
ParamOffset = "offset"
)

const (
ProfileItemOrderBy = "order_by"
ProfileItemOrderDirection = "order_direction"
ProfileItemTags = "tags"
ProfileDefault = "default"
)
3 changes: 1 addition & 2 deletions internal/torznab/torznabfx/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import (
func New() fx.Option {
return fx.Module(
"torznab",
configfx.NewConfigModule[torznab.UntypedConfig]("torznab", torznab.NewDefaultUntypedConfig()),
configfx.NewConfigModule[torznab.Config]("torznab", torznab.NewDefaultConfig()),
fx.Provide(
adapter.New,
torznab.New,
httpserver.New,
),
)
Expand Down

0 comments on commit 35df2c8

Please sign in to comment.