Skip to content

Instantly share code, notes, and snippets.

@diamondburned
Created May 11, 2020 01:06
Show Gist options
  • Save diamondburned/fe9218603f592a0380dc310b34cc04fe to your computer and use it in GitHub Desktop.
Save diamondburned/fe9218603f592a0380dc310b34cc04fe to your computer and use it in GitHub Desktop.
trash
var reflectCache sync.Map
type cacheKey struct {
t reflect.Type
f string
}
func getID(v reflect.Value, thing string) discord.Snowflake {
if !v.IsValid() {
return 0
}
t := v.Type()
if t.Kind() == reflect.Ptr {
v = v.Elem()
// Recheck after dereferring
if !v.IsValid() {
return 0
}
t = v.Type()
}
if t.Kind() != reflect.Struct {
return 0
}
return reflectID(thing, v, t)
}
type reflector struct {
steps []step
thing string
thingID string
}
type step struct {
field int
ptr bool
rec []step
}
func reflectID(thing string, v reflect.Value, t reflect.Type) discord.Snowflake {
r := &reflector{thing: thing}
// copy original type
key := r.thing + t.String()
// check the cache
if instructions, ok := reflectCache.Load(key); ok {
if instructions == nil {
return 0
}
return applyInstructions(v, instructions.([]step))
}
r.thingID = r.thing + "ID"
r.steps = make([]step, 0, 1)
id := r._id(v, t)
if r.steps != nil {
reflectCache.Store(key, r.instructions())
}
return id
}
func applyInstructions(v reflect.Value, instructions []step) discord.Snowflake {
// Use a type here to detect recursion:
// var originalT = v.Type()
var laststep reflect.Value
for i, step := range instructions {
if !v.IsValid() {
return 0
}
if i > 0 && step.ptr {
v = v.Elem()
}
if !v.IsValid() {
// is this the bottom of the instructions?
if i == len(instructions)-1 && step.rec != nil {
for _, ins := range step.rec {
var value = laststep.Field(ins.field)
if ins.ptr {
value = value.Elem()
}
if id := applyInstructions(value, instructions); id.Valid() {
return id
}
}
}
return 0
}
laststep = v
v = laststep.Field(step.field)
}
return discord.Snowflake(v.Int())
}
func (r *reflector) instructions() []step {
if len(r.steps) == 0 {
return nil
}
// var instructions = make([]step, len(r.steps))
// for i := 0; i < len(instructions); i++ {
// instructions[i] = r.steps[len(r.steps)-i-1]
// }
instructions := r.steps
return instructions
}
func (r *reflector) step(s step) {
r.steps = append(r.steps, s)
}
func (r *reflector) _id(v reflect.Value, t reflect.Type) (chID discord.Snowflake) {
numFields := t.NumField()
var ptr bool
var ins = step{field: -1}
for i := 0; i < numFields; i++ {
field := t.Field(i)
fType := field.Type
value := v.Field(i)
ptr = false
if fType.Kind() == reflect.Ptr {
fType = fType.Elem()
value = value.Elem()
ptr = true
}
// does laststep have the same field type?
if fType == t {
ins.rec = append(ins.rec, step{field: i, ptr: ptr})
}
if !value.IsValid() {
continue
}
// If we've already found the field:
if ins.field > 0 {
continue
}
switch fType.Kind() {
case reflect.Struct:
if chID = r._id(value, fType); chID.Valid() {
ins.field = i
ins.ptr = ptr
}
case reflect.Int64:
switch {
case false,
// Contains works with "LastMessageID" and such.
strings.Contains(field.Name, r.thingID),
// Special case where the struct name has Channel in it.
field.Name == "ID" && strings.Contains(t.Name(), r.thing):
ins.field = i
ins.ptr = ptr
chID = discord.Snowflake(value.Int())
}
}
}
// If we've found the field:
r.step(ins)
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment