218 lines
5.1 KiB
Go
218 lines
5.1 KiB
Go
//go:generate go run ./_codegen/main.go --in ../../flags.yml --out ./list.go
|
|
|
|
package feature
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// Flag represents a generic feature flag with a key and a default.
|
|
type Flag interface {
|
|
// Key returns the programmatic backend identifier for the flag.
|
|
Key() string
|
|
// Default returns the type-agnostic zero value for the flag.
|
|
// Type-specific flag implementations may expose a typed default
|
|
// (e.g. BoolFlag includes a boolean Default field).
|
|
Default() interface{}
|
|
// Expose the flag.
|
|
Expose() bool
|
|
}
|
|
|
|
// MakeFlag constructs a Flag. The concrete implementation is inferred from the provided default.
|
|
func MakeFlag(name, key, owner string, defaultValue interface{}, lifetime Lifetime, expose bool) Flag {
|
|
if v, ok := defaultValue.(int); ok {
|
|
defaultValue = int32(v)
|
|
}
|
|
b := MakeBase(name, key, owner, defaultValue, lifetime, expose)
|
|
switch v := defaultValue.(type) {
|
|
case bool:
|
|
return BoolFlag{b, v}
|
|
case float64:
|
|
return FloatFlag{b, v}
|
|
case int32:
|
|
return IntFlag{b, v}
|
|
case string:
|
|
return StringFlag{b, v}
|
|
default:
|
|
return StringFlag{b, fmt.Sprintf("%v", v)}
|
|
}
|
|
}
|
|
|
|
// flag base type.
|
|
type Base struct {
|
|
// name of the flag.
|
|
name string
|
|
// key is the programmatic backend identifier for the flag.
|
|
key string
|
|
// defaultValue for the flag.
|
|
defaultValue interface{}
|
|
// owner is an individual or team responsible for the flag.
|
|
owner string
|
|
// lifetime of the feature flag.
|
|
lifetime Lifetime
|
|
// expose the flag.
|
|
expose bool
|
|
}
|
|
|
|
var _ Flag = Base{}
|
|
|
|
// MakeBase constructs a flag flag.
|
|
func MakeBase(name, key, owner string, defaultValue interface{}, lifetime Lifetime, expose bool) Base {
|
|
return Base{
|
|
name: name,
|
|
key: key,
|
|
owner: owner,
|
|
defaultValue: defaultValue,
|
|
lifetime: lifetime,
|
|
expose: expose,
|
|
}
|
|
}
|
|
|
|
// Key returns the programmatic backend identifier for the flag.
|
|
func (f Base) Key() string {
|
|
return f.key
|
|
}
|
|
|
|
// Default returns the type-agnostic zero value for the flag.
|
|
func (f Base) Default() interface{} {
|
|
return f.defaultValue
|
|
}
|
|
|
|
// Expose the flag.
|
|
func (f Base) Expose() bool {
|
|
return f.expose
|
|
}
|
|
|
|
func (f Base) value(ctx context.Context, flagger ...Flagger) (interface{}, bool) {
|
|
var (
|
|
m map[string]interface{}
|
|
ok bool
|
|
)
|
|
if len(flagger) < 1 {
|
|
m, ok = ctx.Value(featureContextKey).(map[string]interface{})
|
|
} else {
|
|
var err error
|
|
m, err = flagger[0].Flags(ctx, f)
|
|
ok = err == nil
|
|
}
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
v, ok := m[f.Key()]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
return v, true
|
|
}
|
|
|
|
// StringFlag implements Flag for string values.
|
|
type StringFlag struct {
|
|
Base
|
|
defaultString string
|
|
}
|
|
|
|
var _ Flag = StringFlag{}
|
|
|
|
// MakeStringFlag returns a string flag with the given Base and default.
|
|
func MakeStringFlag(name, key, owner string, defaultValue string, lifetime Lifetime, expose bool) StringFlag {
|
|
b := MakeBase(name, key, owner, defaultValue, lifetime, expose)
|
|
return StringFlag{b, defaultValue}
|
|
}
|
|
|
|
// String value of the flag on the request context.
|
|
func (f StringFlag) String(ctx context.Context, flagger ...Flagger) string {
|
|
i, ok := f.value(ctx, flagger...)
|
|
if !ok {
|
|
return f.defaultString
|
|
}
|
|
s, ok := i.(string)
|
|
if !ok {
|
|
return f.defaultString
|
|
}
|
|
return s
|
|
}
|
|
|
|
// FloatFlag implements Flag for float values.
|
|
type FloatFlag struct {
|
|
Base
|
|
defaultFloat float64
|
|
}
|
|
|
|
var _ Flag = FloatFlag{}
|
|
|
|
// MakeFloatFlag returns a string flag with the given Base and default.
|
|
func MakeFloatFlag(name, key, owner string, defaultValue float64, lifetime Lifetime, expose bool) FloatFlag {
|
|
b := MakeBase(name, key, owner, defaultValue, lifetime, expose)
|
|
return FloatFlag{b, defaultValue}
|
|
}
|
|
|
|
// Float value of the flag on the request context.
|
|
func (f FloatFlag) Float(ctx context.Context, flagger ...Flagger) float64 {
|
|
i, ok := f.value(ctx, flagger...)
|
|
if !ok {
|
|
return f.defaultFloat
|
|
}
|
|
v, ok := i.(float64)
|
|
if !ok {
|
|
return f.defaultFloat
|
|
}
|
|
return v
|
|
}
|
|
|
|
// IntFlag implements Flag for integer values.
|
|
type IntFlag struct {
|
|
Base
|
|
defaultInt int32
|
|
}
|
|
|
|
var _ Flag = IntFlag{}
|
|
|
|
// MakeIntFlag returns a string flag with the given Base and default.
|
|
func MakeIntFlag(name, key, owner string, defaultValue int32, lifetime Lifetime, expose bool) IntFlag {
|
|
b := MakeBase(name, key, owner, defaultValue, lifetime, expose)
|
|
return IntFlag{b, defaultValue}
|
|
}
|
|
|
|
// Int value of the flag on the request context.
|
|
func (f IntFlag) Int(ctx context.Context, flagger ...Flagger) int32 {
|
|
i, ok := f.value(ctx, flagger...)
|
|
if !ok {
|
|
return f.defaultInt
|
|
}
|
|
v, ok := i.(int32)
|
|
if !ok {
|
|
return f.defaultInt
|
|
}
|
|
return v
|
|
}
|
|
|
|
// BoolFlag implements Flag for boolean values.
|
|
type BoolFlag struct {
|
|
Base
|
|
defaultBool bool
|
|
}
|
|
|
|
var _ Flag = BoolFlag{}
|
|
|
|
// MakeBoolFlag returns a string flag with the given Base and default.
|
|
func MakeBoolFlag(name, key, owner string, defaultValue bool, lifetime Lifetime, expose bool) BoolFlag {
|
|
b := MakeBase(name, key, owner, defaultValue, lifetime, expose)
|
|
return BoolFlag{b, defaultValue}
|
|
}
|
|
|
|
// Enabled indicates whether flag is true or false on the request context.
|
|
func (f BoolFlag) Enabled(ctx context.Context, flagger ...Flagger) bool {
|
|
i, ok := f.value(ctx, flagger...)
|
|
if !ok {
|
|
return f.defaultBool
|
|
}
|
|
v, ok := i.(bool)
|
|
if !ok {
|
|
return f.defaultBool
|
|
}
|
|
return v
|
|
}
|