valueToTableMap converts a struct to a map of column name to value. If the given type is invalid (not a struct or a pointer to a struct, has invalid table tags, etc.), an error is returned.
(val reflect.Value)
| 427 | // given type is invalid (not a struct or a pointer to a struct, has invalid |
| 428 | // table tags, etc.), an error is returned. |
| 429 | func valueToTableMap(val reflect.Value) (map[string]any, error) { |
| 430 | if !isStructOrStructPointer(val.Type()) { |
| 431 | return nil, xerrors.Errorf("valueToTableMap called with a non-struct or a non-pointer-to-a-struct type") |
| 432 | } |
| 433 | if val.Kind() == reflect.Pointer { |
| 434 | if val.IsNil() { |
| 435 | // No data for this struct, so return an empty map. All values will |
| 436 | // be rendered as nil in the resulting table. |
| 437 | return map[string]any{}, nil |
| 438 | } |
| 439 | |
| 440 | val = val.Elem() |
| 441 | } |
| 442 | |
| 443 | row := map[string]any{} |
| 444 | for i := 0; i < val.NumField(); i++ { |
| 445 | field := val.Type().Field(i) |
| 446 | fieldVal := val.Field(i) |
| 447 | name, _, _, recursive, skip, emptyNil, err := parseTableStructTag(field) |
| 448 | if err != nil { |
| 449 | return nil, xerrors.Errorf("parse struct tags for field %q in type %T: %w", field.Name, val, err) |
| 450 | } |
| 451 | if name == "" { |
| 452 | continue |
| 453 | } |
| 454 | |
| 455 | fieldType := field.Type |
| 456 | |
| 457 | // If empty_nil is set and this is a nil pointer, use a zero value. |
| 458 | if emptyNil && fieldVal.Kind() == reflect.Pointer && fieldVal.IsNil() { |
| 459 | fieldVal = reflect.New(fieldType.Elem()) |
| 460 | } |
| 461 | |
| 462 | // Recurse if it's a struct. |
| 463 | if recursive { |
| 464 | if !isStructOrStructPointer(fieldType) { |
| 465 | return nil, xerrors.Errorf("field %q in type %q is marked as recursive but does not contain a struct or a pointer to a struct", field.Name, fieldType.String()) |
| 466 | } |
| 467 | |
| 468 | // valueToTableMap does nothing on pointers so we don't need to |
| 469 | // filter here. |
| 470 | childMap, err := valueToTableMap(fieldVal) |
| 471 | if err != nil { |
| 472 | return nil, xerrors.Errorf("get child field values for field %q in type %q: %w", field.Name, fieldType.String(), err) |
| 473 | } |
| 474 | for childName, childValue := range childMap { |
| 475 | fullName := fmt.Sprintf("%s %s", name, childName) |
| 476 | if skip { |
| 477 | fullName = childName |
| 478 | } |
| 479 | row[fullName] = childValue |
| 480 | } |
| 481 | continue |
| 482 | } |
| 483 | |
| 484 | // Otherwise, we just use the field value. |
| 485 | row[name] = fieldVal.Interface() |
| 486 | } |
no test coverage detected