Fix it
Pointer vs. value receivers are... interesting.
This commit is contained in:
parent
3e34e25f54
commit
99e372a4db
121
cardsim/stats.go
121
cardsim/stats.go
@ -118,8 +118,9 @@ func (s statFunc[T]) Visible() bool {
|
||||
return s.visible
|
||||
}
|
||||
|
||||
// ExtractStats pulls all exported stats out of a struct. It puts fields before
|
||||
// method.
|
||||
// ExtractStats pulls all exported stats out of a struct. It puts methods before
|
||||
// fields. If the calculated name of a method conflicts with the calculated
|
||||
// name of a stat from a field, the method wins.
|
||||
//
|
||||
// A field is a stat if it is of some Stat type or is tagged with `cardsim:"stat"`,
|
||||
// `cardsim:"hidden"` (invisible stat), `cardsim:"round2"` (or any integer, 2 is
|
||||
@ -148,6 +149,60 @@ func ExtractStats(x any) []Stat {
|
||||
typ := v.Type()
|
||||
|
||||
var ret []Stat
|
||||
|
||||
known := make(map[string]bool)
|
||||
for _, vv := range []reflect.Value{v, v.Addr()} {
|
||||
xt := vv.Type()
|
||||
lim := xt.NumMethod()
|
||||
for i := 0; i < lim; i++ {
|
||||
m := xt.Method(i)
|
||||
if !m.IsExported() {
|
||||
continue
|
||||
}
|
||||
tm := m.Type
|
||||
if tm.NumIn() != 1 {
|
||||
// 1 arg -- receiver
|
||||
continue
|
||||
}
|
||||
if tm.NumOut() != 1 {
|
||||
continue
|
||||
}
|
||||
nameParts := explode(m.Name)
|
||||
if len(nameParts) < 2 {
|
||||
continue
|
||||
}
|
||||
isHidden := false
|
||||
if nameParts[0] == "Hidden" {
|
||||
isHidden = true
|
||||
nameParts = nameParts[1:]
|
||||
}
|
||||
if nameParts[0] != "Stat" {
|
||||
continue
|
||||
}
|
||||
n := strings.Join(nameParts[1:], " ")
|
||||
if n == "" {
|
||||
continue
|
||||
}
|
||||
if known[n] {
|
||||
continue
|
||||
}
|
||||
known[n] = true
|
||||
val := vv.Method(i).Call([]reflect.Value{})
|
||||
if len(val) != 1 {
|
||||
// This shouldn't happen - we already checked Out. Weird.
|
||||
continue
|
||||
}
|
||||
if !val[0].CanInterface() {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, &StatLiteral{
|
||||
Name: n,
|
||||
Value: fmt.Sprint(val[0].Interface()),
|
||||
IsVisible: !isHidden,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fields := reflect.VisibleFields(typ)
|
||||
for _, sf := range fields {
|
||||
if !sf.IsExported() {
|
||||
@ -157,7 +212,12 @@ func ExtractStats(x any) []Stat {
|
||||
if !f.CanInterface() {
|
||||
continue
|
||||
}
|
||||
if s, ok := f.Interface().(Stat); ok {
|
||||
iface := f.Interface()
|
||||
if s, ok := iface.(Stat); ok {
|
||||
if known[s.StatName()] {
|
||||
continue
|
||||
}
|
||||
known[s.StatName()] = true
|
||||
ret = append(ret, s)
|
||||
continue
|
||||
}
|
||||
@ -181,14 +241,19 @@ func ExtractStats(x any) []Stat {
|
||||
t = t[5:]
|
||||
n, _ := strconv.Atoi(t)
|
||||
fs := fmt.Sprintf("%%.%df", n)
|
||||
val = fmt.Sprintf(fs, f.Interface())
|
||||
val = fmt.Sprintf(fs, iface)
|
||||
} else if isStat {
|
||||
val = fmt.Sprint(f.Interface())
|
||||
val = fmt.Sprint(iface)
|
||||
} else {
|
||||
continue // not identifiably a stat
|
||||
}
|
||||
nm := strings.Join(explode(sf.Name), " ")
|
||||
if known[nm] {
|
||||
continue
|
||||
}
|
||||
known[nm] = true
|
||||
ret = append(ret, &StatLiteral{
|
||||
Name: strings.Join(explode(sf.Name), " "),
|
||||
Name: nm,
|
||||
Value: val,
|
||||
IsVisible: !isHidden,
|
||||
})
|
||||
@ -197,50 +262,6 @@ func ExtractStats(x any) []Stat {
|
||||
// Else, not a stat.
|
||||
}
|
||||
|
||||
lim := typ.NumMethod()
|
||||
for i := 0; i < lim; i++ {
|
||||
m := typ.Method(i)
|
||||
if !m.IsExported() {
|
||||
continue
|
||||
}
|
||||
tm := m.Type
|
||||
if tm.NumIn() != 1 {
|
||||
// 1 arg -- receiver
|
||||
continue
|
||||
}
|
||||
if tm.NumOut() != 1 {
|
||||
continue
|
||||
}
|
||||
nameParts := explode(m.Name)
|
||||
if len(nameParts) < 2 {
|
||||
continue
|
||||
}
|
||||
isHidden := false
|
||||
if nameParts[0] == "Hidden" {
|
||||
isHidden = true
|
||||
nameParts = nameParts[1:]
|
||||
}
|
||||
if nameParts[0] != "Stat" {
|
||||
continue
|
||||
}
|
||||
n := strings.Join(nameParts[1:], ": ")
|
||||
if n == "" {
|
||||
continue
|
||||
}
|
||||
val := v.Method(i).Call([]reflect.Value{})
|
||||
if len(val) != 1 {
|
||||
// This shouldn't happen - we already checked Out. Weird.
|
||||
continue
|
||||
}
|
||||
if !val[0].CanInterface() {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, &StatLiteral{
|
||||
Name: n,
|
||||
Value: fmt.Sprint(val[0].Interface()),
|
||||
IsVisible: !isHidden,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ type SmokeTestCollection struct {
|
||||
Things int `cardsim:"stat"`
|
||||
MoreThings int `cardsim:"hidden"`
|
||||
FloatyThings float64 `cardsim:"round1"`
|
||||
Label string `cardsim:"stat"`
|
||||
}
|
||||
|
||||
func (c *SmokeTestCollection) Average() float64 {
|
||||
|
@ -28,6 +28,10 @@ func main() {
|
||||
Name: "Flavor",
|
||||
Value: "Lemon",
|
||||
},
|
||||
Things: 5,
|
||||
MoreThings: 9,
|
||||
FloatyThings: 123.456,
|
||||
Label: "whee",
|
||||
},
|
||||
)
|
||||
p.Name = "Dave"
|
||||
|
Loading…
Reference in New Issue
Block a user