Remiplement Add to use reflection.

Now it matters whether `FieldLabel`s are spelled correctly, but in return, we don't have to write and maintain that obnoxious `switch` statement.
This commit is contained in:
Kistaro Windrider 2024-09-29 12:19:07 -07:00
parent 8a28f38d4d
commit 2d16f97314
Signed by: kistaro
SSH Key Fingerprint: SHA256:TBE2ynfmJqsAf0CP6gsflA0q5X5wD5fVKWPsZ7eVUg8
2 changed files with 52 additions and 52 deletions

View File

@ -9,7 +9,6 @@ import (
var ( var (
ErrOptionNotEnabled = errors.New("option not enabled") ErrOptionNotEnabled = errors.New("option not enabled")
ErrPolicyNotEnacted = errors.New("cannot unenact policy that is not enacted") ErrPolicyNotEnacted = errors.New("cannot unenact policy that is not enacted")
ErrNoFieldLabel = errors.New("field does not exist")
// ErrUnimplemented and ErrKeepMessaage are "non-errors". They are used // ErrUnimplemented and ErrKeepMessaage are "non-errors". They are used
// as special signals that the result needs to be handled in a special way; // as special signals that the result needs to be handled in a special way;

View File

@ -1,7 +1,9 @@
package koboldsim package koboldsim
import ( import (
"errors"
"fmt" "fmt"
"reflect"
"git.chromaticdragon.app/kistaro/CardSimEngine/cardsim" "git.chromaticdragon.app/kistaro/CardSimEngine/cardsim"
) )
@ -167,6 +169,10 @@ func NewKoboldMine() *KoboldMine {
} }
} }
// FieldLabel instances are strings exactly matching the name of an
// exported field in `KoboldMine`. These are used to map field names to
// amounts to change in `TablePolicy` instances, which are then looked
// up by name (via reflection) when adding the stat.
type FieldLabel string type FieldLabel string
const ( const (
@ -188,56 +194,51 @@ const (
Secrecy FieldLabel = "Secrecy" Secrecy FieldLabel = "Secrecy"
) )
func (k *KoboldMine) Add(which FieldLabel, amount float64) error { // ErrBadFieldLabel is an "error category" for all errors where a
switch which { // FieldLabel did not correctly name a Field that could be used in Add.
case Authoritarianism: var ErrBadFieldLabel = errors.New("bad field label")
k.Authoritarianism += amount
return nil // ErrNoFieldLabel is a kind of `ErrBadFieldLabel` used when no field
case BasePopulation: // of the target has the exact name specified in the FieldLabel. Check
k.BasePopulation += amount // spelling and capitalization.
return nil var ErrNoFieldLabel = fmt.Errorf("%w: field does not exist", ErrBadFieldLabel)
case Bureaucracy:
k.Bureaucracy += amount // ErrFieldNotFloat is a kind of `ErrBadFieldLabel` used when it is not
return nil // possible to read and assign a float to the named field. Is this the
case Construction: // name of a calculated stat (a function) rather than a base stat?
k.Construction += amount var ErrFieldNotFloat = fmt.Errorf("%w: field type is not float", ErrBadFieldLabel)
return nil
case Cruelty: // ErrFieldSetPanic is a kind of `ErrBadFieldLabel` used when trying to
k.Cruelty += amount // set the value of a field panicked. The panic message should be
return nil // preserved in the error to diagnose the problem. If the problem is
case Education: // that the field is unexported, capitalize the name (including inside
k.Education += amount // the KoboldMine type). Any other issue is a more complicated bug,
return nil // because all of them that aren't already caught by ErrFieldNotFloat
case FoodSupply: // imply something very unexpected happened with `k` itself (in `Add`).
k.FoodSupply += amount var ErrFieldSetPanic = fmt.Errorf("%w: panic when setting", ErrBadFieldLabel)
return nil
case ForeignRelations: // Use a FieldLabel to add an amount to the matching field. If no such
k.ForeignRelations += amount // field can be found, the field is not exported, or the field is not
return nil // of type float64, this returns an error ad does not change any values.
case Gullibility: func (k *KoboldMine) Add(which FieldLabel, amount float64) (finalErr error) {
k.Gullibility += amount kv := reflect.ValueOf(k).Elem()
return nil f := kv.FieldByName(string(which))
case Madness: if !f.IsValid() {
k.Madness += amount return fmt.Errorf("cannot add %f to field %q: %w", amount, which, ErrNoFieldLabel)
return nil }
case Manufacturing: if !f.CanFloat() {
k.Manufacturing += amount return fmt.Errorf("cannot add %f to field %q: %w", amount, which, ErrFieldNotFloat)
return nil }
case Mining:
k.Mining += amount defer func() {
return nil if r := recover(); r != nil {
case Militarism: if e, ok := r.(error); ok {
k.Militarism += amount finalErr = fmt.Errorf("could not add %f to field %q: %w: %w", amount, which, ErrFieldSetPanic, e)
return nil } else {
case Rebellion: finalErr = fmt.Errorf("could not add %f to field %q: %w: %v", amount, which, ErrFieldSetPanic, r)
k.Rebellion += amount }
return nil }
case Scavenging: }()
k.Scavenging += amount f.SetFloat(f.Float() + amount)
return nil
case Secrecy:
k.Secrecy += amount
return nil return nil
} }
return fmt.Errorf("cannot add %f to %q: %w", amount, which, ErrNoFieldLabel)
}