Search code examples
goredisredigo

Implementing Redigo Scanner interface on a struct's field


I have a struct that looks like this:

type authEnum int

const (
    never authEnum = iota
    sometimes
    always
)

type Attrs struct {
    Secret         string   `redis:"secret"`
    RequireSecret  authEnum `redis:"requireSecret"`
    UserID         string   `redis:"userId"`
}

func (e *authEnum) RedisScan(src interface{}) error {
    // This never gets called!
    if e == nil {
        return fmt.Errorf("nil pointer")
    }
    switch src := src.(type) {
    case string:
        if src == "false" || src == "never" {
            *e = never
        } else if src == "sometimes" {
            *e = sometimes
        } else { // "always" or "true"
            *e = always
        }
    default:
        return fmt.Errorf("cannot convert authEnum from %T to %T", src, e)
    }
    return nil
}

func getAttributes(ctx *AppContext, hash string) (*Attrs, error) {
    rc := ctx.RedisPool.Get()
    values, err := redis.Values(rc.Do("HGETALL", "redishash"))
    rc.Close()
    if err != nil {
        return nil, err
    }
    attrs := Attrs{}
    redis.ScanStruct(values, &attrs)
    return &attrs, nil
}

How do I implement the Scanner interface on the RequireSecret attribute to parse an authEnum type out of "never", "sometimes" or "always" redis hash values?

How do I calculate the value and assign it to the authEnum? In my code example RedisScan never gets called.


Solution

  • Implement the method on a pointer receiver. Redis bulk strings are represented as []byte, not string:

    func (e *authEnum) RedisScan(src interface{}) error {
        b, ok := src.([]byte)
        if !ok {
            return fmt.Errorf("cannot convert authEnum from %T to %T", src, b)
        }
        switch string(b) {
        case "false", "never":
            *e = never
        case "sometimes":
            *e = sometimes
        default:
            *e = always
        }
        return nil
    }
    

    Always check and handle errors. The error returned from ScanStruct reports the type problem.

    There's no need to check for nil pointer to the struct member. If the argument to ScanStruct is nil, then Redigo will panic well before the RedisScan method is called.