package redisactivity import ( "errors" "fmt" "reflect" "regexp" "strings" "time" "github.com/redis/go-redis/v9" "proxy-pool/internal/platform/credentials" ) var ( ErrInvalidOptions = errors.New("invalid redis activity adapter options") namespacePattern = regexp.MustCompile(`^[A-Za-z0-9._-]+$`) ) type Options struct { Namespace string Credentials credentials.Store OperationTTL time.Duration MaxCandidateScan int MaxRuntimeCounters int MaxInventoryScan int CleanupLimit int } type Adapter struct { client redis.Scripter credentials credentials.Store keys keyspace options Options } func New(client redis.Scripter, options Options) (*Adapter, error) { options.Namespace = strings.TrimSpace(options.Namespace) if options.MaxRuntimeCounters == 0 { options.MaxRuntimeCounters = options.MaxCandidateScan } if options.MaxInventoryScan == 0 { options.MaxInventoryScan = options.MaxCandidateScan } if nilInterface(client) || nilInterface(options.Credentials) || !namespacePattern.MatchString(options.Namespace) || options.OperationTTL <= 0 || options.MaxCandidateScan <= 0 || options.MaxRuntimeCounters <= 0 || options.MaxInventoryScan <= 0 || options.CleanupLimit <= 0 { return nil, ErrInvalidOptions } return &Adapter{ client: client, credentials: options.Credentials, keys: newKeyspace(options.Namespace), options: options, }, nil } func (a *Adapter) Format(state fmt.State, _ rune) { if a == nil { _, _ = state.Write([]byte("redisactivity.Adapter")) return } _, _ = fmt.Fprintf(state, "redisactivity.Adapter{Namespace:%q}", a.options.Namespace) } func nilInterface(value any) bool { if value == nil { return true } reflected := reflect.ValueOf(value) switch reflected.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: return reflected.IsNil() default: return false } }