Fix it
Pointer vs. value receivers are... interesting.
This commit is contained in:
		
							
								
								
									
										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 | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user