mergeZero merges the fields of src into dst, but only if the field in dst is currently the zero value. Make sure `dst` is a pointer to a struct, otherwise the fields are not assignable.
(dst any, src any)
| 30 | // currently the zero value. |
| 31 | // Make sure `dst` is a pointer to a struct, otherwise the fields are not assignable. |
| 32 | func mergeZero(dst any, src any) { |
| 33 | srcv := reflect.ValueOf(src) |
| 34 | if srcv.Kind() == reflect.Ptr { |
| 35 | srcv = srcv.Elem() |
| 36 | } |
| 37 | remain := [][2]reflect.Value{ |
| 38 | {reflect.ValueOf(dst).Elem(), srcv}, |
| 39 | } |
| 40 | |
| 41 | // Traverse the struct fields and set them only if they are currently zero. |
| 42 | // This is a breadth-first traversal of the struct fields. Struct definitions |
| 43 | // Should not be that deep, so we should not hit any stack overflow issues. |
| 44 | for { |
| 45 | if len(remain) == 0 { |
| 46 | return |
| 47 | } |
| 48 | dv, sv := remain[0][0], remain[0][1] |
| 49 | remain = remain[1:] // |
| 50 | for i := 0; i < dv.NumField(); i++ { |
| 51 | df := dv.Field(i) |
| 52 | sf := sv.Field(i) |
| 53 | if !df.CanSet() { |
| 54 | continue |
| 55 | } |
| 56 | if df.IsZero() { // only write if currently zero |
| 57 | df.Set(sf) |
| 58 | continue |
| 59 | } |
| 60 | |
| 61 | if dv.Field(i).Kind() == reflect.Struct { |
| 62 | // If the field is a struct, we need to traverse it as well. |
| 63 | remain = append(remain, [2]reflect.Value{df, sf}) |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | } |