flattenStructFields will return all top level fields for a given structure. Only anonymously embedded structs will be recursively flattened such that their fields are returned as top level fields. Named nested structs will be returned as a single field. Conflicting field names need to be handled by
(leftV, rightV reflect.Value)
| 184 | // as a single field. |
| 185 | // Conflicting field names need to be handled by the caller. |
| 186 | func flattenStructFields(leftV, rightV reflect.Value) ([]fieldDiff, error) { |
| 187 | // Dereference pointers if the field is a pointer field. |
| 188 | if leftV.Kind() == reflect.Ptr { |
| 189 | leftV = derefPointer(leftV) |
| 190 | rightV = derefPointer(rightV) |
| 191 | } |
| 192 | |
| 193 | if leftV.Kind() != reflect.Struct { |
| 194 | return nil, xerrors.Errorf("%q is not a struct, kind=%s", leftV.String(), leftV.Kind()) |
| 195 | } |
| 196 | |
| 197 | var allFields []fieldDiff |
| 198 | rightT := rightV.Type() |
| 199 | |
| 200 | // Loop through all top level fields of the struct. |
| 201 | for i := 0; i < rightT.NumField(); i++ { |
| 202 | if !rightT.Field(i).IsExported() { |
| 203 | continue |
| 204 | } |
| 205 | |
| 206 | var ( |
| 207 | leftF = leftV.Field(i) |
| 208 | rightF = rightV.Field(i) |
| 209 | ) |
| 210 | |
| 211 | if rightT.Field(i).Anonymous { |
| 212 | // Anonymous fields are recursively flattened. |
| 213 | anonFields, err := flattenStructFields(leftF, rightF) |
| 214 | if err != nil { |
| 215 | return nil, xerrors.Errorf("flatten anonymous field %q: %w", rightT.Field(i).Name, err) |
| 216 | } |
| 217 | allFields = append(allFields, anonFields...) |
| 218 | continue |
| 219 | } |
| 220 | |
| 221 | // Single fields append as is. |
| 222 | allFields = append(allFields, fieldDiff{ |
| 223 | LeftF: leftF, |
| 224 | RightF: rightF, |
| 225 | FieldType: rightT.Field(i), |
| 226 | }) |
| 227 | } |
| 228 | return allFields, nil |
| 229 | } |
| 230 | |
| 231 | // derefPointer deferences a reflect.Value that is a pointer to its underlying |
| 232 | // value. It dereferences recursively until it finds a non-pointer value. If the |
no test coverage detected