Skip to content

Commit

Permalink
feat: enable settings.Blueprint to be both ptr and copy
Browse files Browse the repository at this point in the history
Signed-off-by: Marko Kungla <marko@mkungla.dev>
  • Loading branch information
mkungla committed Jun 2, 2024
1 parent fb68555 commit d3c3067
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 82 deletions.
59 changes: 41 additions & 18 deletions pkg/settings/blueprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,20 @@ func (b *Blueprint) Migrate(keyfrom, keyto string) error {
b.migrations[keyfrom] = keyto
return nil
}
func (b *Blueprint) settingSpecFromField(field reflect.StructField, value reflect.Value) (SettingSpec, error) {

func (b *Blueprint) settingSpecFromField(field reflect.StructField, value reflect.Value) (SettingSpec, error) {
spec := SettingSpec{}

spec.IsSet = isFieldSet(value)

var persistent string
spec.Key, persistent, _ = strings.Cut(field.Tag.Get("key"), ",")

if persistent == "save" {
spec.Persistent = true
}
if persistent == "config" {
spec.UserDefined = true
}

// spec.Persistent
// Use struct field name converted to dot.separated.format if 'key' tag is not present
if spec.Key == "" {
spec.Key = toUndersCoreSeparated(field.Name)
Expand All @@ -101,8 +101,19 @@ func (b *Blueprint) settingSpecFromField(field reflect.StructField, value reflec
spec.Mutability = SettingImmutable
spec.IsSet = true
spec.Kind = KindSettings

// Handle nested settings
var err error
spec.Settings, err = callBlueprintIfImplementsSettings(value)
if value.Kind() == reflect.Ptr {
// If the value is a nil pointer, initialize it
if value.IsNil() {
value.Set(reflect.New(value.Type().Elem()))
}
spec.Settings, err = New(value.Interface().(Settings))
} else {
spec.Settings, err = New(value.Addr().Interface().(Settings))
}

if err != nil {
return spec, err
}
Expand All @@ -117,6 +128,7 @@ func (b *Blueprint) settingSpecFromField(field reflect.StructField, value reflec
spec.Mutability = SettingMutable
default:
spec.Mutability = SettingImmutable
spec.IsSet = true
}

kindGetterMethod := value.MethodByName("SettingKind")
Expand All @@ -130,28 +142,39 @@ func (b *Blueprint) settingSpecFromField(field reflect.StructField, value reflec
spec.Kind = KindCustom
}

desc := field.Tag.Get("desc")
if desc != "" {
if spec.i18n == nil {
spec.i18n = make(map[language.Tag]string)
}
spec.i18n[language.English] = desc
}
spec.Default = field.Tag.Get("default")
if spec.Kind == KindBool && (spec.Default != "" && spec.Default != "false") {
return spec, fmt.Errorf("%w: %q boolean field %q can have default value only false", ErrBlueprint, b.pkg, spec.Key)
}

if spec.IsSet {
spec.Value = getStringValue(value)
if isFieldSet(value) {
// if the field is set by the developer, then set it as the default value
val := getStringValue(value)
spec.Value = val
spec.Default = val
} else {
spec.Value = spec.Default
}

if value.CanInterface() {
if unmarshaller, ok := value.Addr().Interface().(Unmarshaller); ok {
// Check if value implements Marshaller and Unmarshaller
if value.CanAddr() {
ptr := value.Addr().Interface()
if unmarshaller, ok := ptr.(Unmarshaller); ok {
spec.Unmarchaler = unmarshaller
}
if marshaller, ok := ptr.(Marshaller); ok {
spec.Marchaler = marshaller
}
}
if marshaller, ok := value.Interface().(Marshaller); ok {
spec.Marchaler = marshaller
}

if spec.Unmarchaler == nil {
return spec, fmt.Errorf("%w: %q field %q must implement either SettingField interface missing UnmarshalSetting", ErrBlueprint, b.pkg, spec.Key)
}
if spec.Marchaler == nil {
return spec, fmt.Errorf("%w: %q field %q must implement either SettingField interface missing MarshalSetting", ErrBlueprint, b.pkg, spec.Key)
if spec.Unmarchaler == nil || spec.Marchaler == nil {
return spec, fmt.Errorf("%w: %q field %q must implement SettingField interface (both UnmarshalSetting and MarshalSetting methods required)", ErrBlueprint, b.pkg, spec.Key)
}
} else {
return spec, fmt.Errorf("%w: %q field %q must implement either Settings or SettingField interface", ErrBlueprint, b.pkg, spec.Key)
Expand Down
9 changes: 7 additions & 2 deletions pkg/settings/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ func (k Kind) String() string {
type String string

// MarshalSetting converts the String setting to a byte slice for storage or transmission.
func (s String) MarshalSetting() ([]byte, error) {
func (s *String) MarshalSetting() ([]byte, error) {
// Simply cast the String to a byte slice.
return []byte(s), nil
return []byte(*s), nil
}

// UnmarshalSetting updates the String setting from a byte slice, typically read from storage or received in a message.
Expand All @@ -54,10 +54,12 @@ func (s *String) UnmarshalSetting(data []byte) error {
return nil
}

// Stringer implementation for String type
func (s String) String() string {
return string(s)
}

// SettingKind returns the kind of setting
func (s String) SettingKind() Kind {
return KindString
}
Expand All @@ -72,6 +74,9 @@ func (b Bool) MarshalSetting() ([]byte, error) {

// UnmarshalSetting updates the Bool setting from a byte slice, interpreting it as "true" or "false".
func (b *Bool) UnmarshalSetting(data []byte) error {
if len(data) == 0 {
return nil
}
// Parse the byte slice as a boolean and store it in Bool.
val, err := strconv.ParseBool(string(data))
if err != nil {
Expand Down
35 changes: 30 additions & 5 deletions pkg/settings/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (p *Profile) Has(key string) bool {
return ok
}

func (p *Profile) Set(key string, val SettingField) (err error) {
func (p *Profile) Set(key string, val any) (err error) {
if !p.Has(key) {
return fmt.Errorf("setting not found %s", key)
}
Expand All @@ -110,18 +110,42 @@ func (p *Profile) Set(key string, val SettingField) (err error) {
} else if setting.isSet && setting.mutability == SettingOnce {
return fmt.Errorf("setting is set once %s", key)
}
setting.vv, err = vars.NewAs(key, val, true, vars.Kind(setting.kind))
if err != nil {
return fmt.Errorf("%w: key(%s) %s", ErrProfile, key, err.Error())
}

for _, v := range p.schema.settings[key].validators {
if err := v.fn(setting); err != nil {
return err
}
}
setting.vv, err = vars.NewAs(key, val.String(), true, vars.Kind(setting.kind))

setting.isSet = true

p.settings[key] = setting
return nil
}

func (p *Profile) Validate(key string, val any) (err error) {
if !p.Has(key) {
return fmt.Errorf("setting not found %s", key)
}
p.mu.RLock()
defer p.mu.RUnlock()
setting := p.settings[key]

setting.vv, err = vars.NewAs(key, val, true, vars.Kind(setting.kind))
if err != nil {
return fmt.Errorf("%w: key(%s) %s", ErrProfile, key, err.Error())
}
setting.isSet = true

p.settings[key] = setting
for _, v := range p.schema.settings[key].validators {
if err := v.fn(setting); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -165,7 +189,8 @@ func (p *Profile) load(prefs *Preferences) (err error) {
}
p.settings[lkey] = s
} else {
return fmt.Errorf("%w: preferences provided key(%s) not found", ErrProfile, lkey)
// return fmt.Errorf("%w: preferences provided key(%s) not found", ErrProfile, lkey)
prefs.data[lkey] = ""
}
}
}
Expand Down
33 changes: 20 additions & 13 deletions pkg/settings/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type SettingSpec struct {
Value string
Required bool
Persistent bool
UserDefined bool
Unmarchaler Unmarshaller
Marchaler Marshaller
Settings *Blueprint
Expand Down Expand Up @@ -88,11 +89,12 @@ func (s SettingSpec) Setting(lang language.Tag) (Setting, error) {

func (s SettingSpec) setting() (Setting, error) {
setting := Setting{
key: s.Key,
kind: s.Kind,
isSet: s.IsSet,
mutability: s.Mutability,
persistent: s.Persistent,
key: s.Key,
kind: s.Kind,
isSet: s.IsSet,
mutability: s.Mutability,
persistent: s.Persistent,
userDefined: s.UserDefined,
}

var err error
Expand All @@ -108,14 +110,15 @@ func (s SettingSpec) setting() (Setting, error) {
}

type Setting struct {
key string
vv vars.Variable
dvv vars.Variable
kind Kind
isSet bool
mutability Mutability
persistent bool
desc string
key string
vv vars.Variable
dvv vars.Variable
kind Kind
isSet bool
mutability Mutability
persistent bool
userDefined bool
desc string
}

func (s Setting) String() string {
Expand Down Expand Up @@ -146,6 +149,10 @@ func (s Setting) Persistent() bool {
return s.persistent
}

func (s Setting) UserDefined() bool {
return s.userDefined
}

func (s Setting) Mutability() Mutability {
return s.mutability
}
Expand Down
Loading

0 comments on commit d3c3067

Please sign in to comment.