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