From 35df2c8722350a6914456f2a916bf113942036a6 Mon Sep 17 00:00:00 2001 From: rraymondgh Date: Fri, 24 Jan 2025 16:38:04 +0000 Subject: [PATCH] typed config --- .../config/configresolver/coerce.go | 8 ++ internal/torznab/config.go | 105 +++++++----------- internal/torznab/httpserver/httpserver.go | 20 ++-- internal/torznab/parameters.go | 7 -- internal/torznab/torznabfx/module.go | 3 +- 5 files changed, 59 insertions(+), 84 deletions(-) diff --git a/internal/boilerplate/config/configresolver/coerce.go b/internal/boilerplate/config/configresolver/coerce.go index 25be5edc..96da9818 100644 --- a/internal/boilerplate/config/configresolver/coerce.go +++ b/internal/boilerplate/config/configresolver/coerce.go @@ -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: diff --git a/internal/torznab/config.go b/internal/torznab/config.go index f8539cf2..ae99797b 100644 --- a/internal/torznab/config.go +++ b/internal/torznab/config.go @@ -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 } diff --git a/internal/torznab/httpserver/httpserver.go b/internal/torznab/httpserver/httpserver.go index 3285d215..b679f32a 100644 --- a/internal/torznab/httpserver/httpserver.go +++ b/internal/torznab/httpserver/httpserver.go @@ -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 { @@ -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 { @@ -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) { @@ -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 { @@ -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 } diff --git a/internal/torznab/parameters.go b/internal/torznab/parameters.go index f0a5f6a3..abcf896d 100644 --- a/internal/torznab/parameters.go +++ b/internal/torznab/parameters.go @@ -11,10 +11,3 @@ const ( ParamLimit = "limit" ParamOffset = "offset" ) - -const ( - ProfileItemOrderBy = "order_by" - ProfileItemOrderDirection = "order_direction" - ProfileItemTags = "tags" - ProfileDefault = "default" -) diff --git a/internal/torznab/torznabfx/module.go b/internal/torznab/torznabfx/module.go index 47bf39fb..7935ccf3 100644 --- a/internal/torznab/torznabfx/module.go +++ b/internal/torznab/torznabfx/module.go @@ -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, ), )