Pointer vs. value receivers are... interesting.
This commit is contained in:
Kistaro Windrider 2023-04-04 11:37:02 -07:00
parent 3e34e25f54
commit 99e372a4db
Signed by: kistaro
SSH Key Fingerprint: SHA256:TBE2ynfmJqsAf0CP6gsflA0q5X5wD5fVKWPsZ7eVUg8
3 changed files with 76 additions and 50 deletions

View File

@ -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,58 +149,13 @@ func ExtractStats(x any) []Stat {
typ := v.Type()
var ret []Stat
fields := reflect.VisibleFields(typ)
for _, sf := range fields {
if !sf.IsExported() {
continue
}
f := v.FieldByIndex(sf.Index)
if !f.CanInterface() {
continue
}
if s, ok := f.Interface().(Stat); ok {
ret = append(ret, s)
continue
}
if t := sf.Tag.Get("cardsim"); t != "" {
isStat := false
isHidden := false
t = strings.ToLower(t)
t = strings.TrimSpace(t)
if strings.HasPrefix(t, "hidden") {
isStat = true
isHidden = true
t = t[6:]
}
if strings.HasPrefix(t, "stat") {
isStat = true
t = t[4:]
}
var val string
if strings.HasPrefix(t, "round") {
isStat = true
t = t[5:]
n, _ := strconv.Atoi(t)
fs := fmt.Sprintf("%%.%df", n)
val = fmt.Sprintf(fs, f.Interface())
} else if isStat {
val = fmt.Sprint(f.Interface())
} else {
continue // not identifiably a stat
}
ret = append(ret, &StatLiteral{
Name: strings.Join(explode(sf.Name), " "),
Value: val,
IsVisible: !isHidden,
})
continue
}
// Else, not a stat.
}
lim := typ.NumMethod()
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 := typ.Method(i)
m := xt.Method(i)
if !m.IsExported() {
continue
}
@ -223,11 +179,15 @@ func ExtractStats(x any) []Stat {
if nameParts[0] != "Stat" {
continue
}
n := strings.Join(nameParts[1:], ": ")
n := strings.Join(nameParts[1:], " ")
if n == "" {
continue
}
val := v.Method(i).Call([]reflect.Value{})
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
@ -241,6 +201,67 @@ func ExtractStats(x any) []Stat {
IsVisible: !isHidden,
})
}
}
fields := reflect.VisibleFields(typ)
for _, sf := range fields {
if !sf.IsExported() {
continue
}
f := v.FieldByIndex(sf.Index)
if !f.CanInterface() {
continue
}
iface := f.Interface()
if s, ok := iface.(Stat); ok {
if known[s.StatName()] {
continue
}
known[s.StatName()] = true
ret = append(ret, s)
continue
}
if t := sf.Tag.Get("cardsim"); t != "" {
isStat := false
isHidden := false
t = strings.ToLower(t)
t = strings.TrimSpace(t)
if strings.HasPrefix(t, "hidden") {
isStat = true
isHidden = true
t = t[6:]
}
if strings.HasPrefix(t, "stat") {
isStat = true
t = t[4:]
}
var val string
if strings.HasPrefix(t, "round") {
isStat = true
t = t[5:]
n, _ := strconv.Atoi(t)
fs := fmt.Sprintf("%%.%df", n)
val = fmt.Sprintf(fs, iface)
} else if isStat {
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: nm,
Value: val,
IsVisible: !isHidden,
})
continue
}
// Else, not a stat.
}
return ret
}

View File

@ -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 {

View File

@ -28,6 +28,10 @@ func main() {
Name: "Flavor",
Value: "Lemon",
},
Things: 5,
MoreThings: 9,
FloatyThings: 123.456,
Label: "whee",
},
)
p.Name = "Dave"