transformUnion generates Go code to transform source union to target union. Note: transport to/from service transforms are always object to union or union to object. The only case a transform is union to union is when converting a projected type from/to a service type.
(source, target *expr.AttributeExpr, sourceVar, targetVar string, newVar bool, ta *TransformAttrs)
| 445 | // union to object. The only case a transform is union to union is when |
| 446 | // converting a projected type from/to a service type. |
| 447 | func transformUnion(source, target *expr.AttributeExpr, sourceVar, targetVar string, newVar bool, ta *TransformAttrs) (string, error) { |
| 448 | if !expr.IsUnion(target.Type) { |
| 449 | return "", fmt.Errorf("cannot transform union %s to non-union %s", source.Type.Name(), target.Type.Name()) |
| 450 | } |
| 451 | srcUnion, tgtUnion := expr.AsUnion(source.Type), expr.AsUnion(target.Type) |
| 452 | if len(srcUnion.Values) != len(tgtUnion.Values) { |
| 453 | return "", fmt.Errorf("cannot transform union: number of union types differ (%s has %d, %s has %d)", |
| 454 | source.Type.Name(), len(srcUnion.Values), target.Type.Name(), len(tgtUnion.Values)) |
| 455 | } |
| 456 | for i, st := range srcUnion.Values { |
| 457 | if err := IsCompatible(st.Attribute.Type, tgtUnion.Values[i].Attribute.Type, sourceVar, targetVar); err != nil { |
| 458 | return "", fmt.Errorf("cannot transform union %s to %s: type at index %d: %w", |
| 459 | source.Type.Name(), target.Type.Name(), i, err) |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | // Unions are generated as concrete sum-type structs with Kind/AsX/SetX |
| 464 | // helpers. Transform by branching on the runtime Kind discriminator. |
| 465 | unionPkg := ta.TargetCtx.Pkg(target) |
| 466 | typeRef := ta.TargetCtx.Scope.Ref(target, unionPkg) |
| 467 | |
| 468 | // Use deterministic temp var: 'obj' at top-level, 'tmp' for nested assignments. |
| 469 | tempVarName := "obj" |
| 470 | if strings.HasPrefix(targetVar, "obj.") { |
| 471 | tempVarName = "tmp" |
| 472 | } |
| 473 | |
| 474 | cases := make([]map[string]any, 0, len(srcUnion.Values)) |
| 475 | for i, st := range srcUnion.Values { |
| 476 | if st == nil || st.Attribute == nil { |
| 477 | continue |
| 478 | } |
| 479 | if i >= len(tgtUnion.Values) { |
| 480 | break |
| 481 | } |
| 482 | tt := tgtUnion.Values[i] |
| 483 | if tt == nil || tt.Attribute == nil { |
| 484 | continue |
| 485 | } |
| 486 | castPkg := ta.TargetCtx.Pkg(tt.Attribute) |
| 487 | // When generating transforms outside of the type's package, some nested |
| 488 | // helper user types may not carry struct:pkg:path metadata. In that case |
| 489 | // default to the union type package rather than the current file package. |
| 490 | if castPkg == ta.TargetCtx.DefaultPkg && unionPkg != "" && unionPkg != ta.TargetCtx.DefaultPkg { |
| 491 | castPkg = unionPkg |
| 492 | } |
| 493 | useHelper := false |
| 494 | if _, ok := st.Attribute.Type.(expr.UserType); ok && expr.IsObject(st.Attribute.Type) { |
| 495 | if _, ok := tt.Attribute.Type.(expr.UserType); ok && expr.IsObject(tt.Attribute.Type) { |
| 496 | useHelper = true |
| 497 | } |
| 498 | } |
| 499 | cases = append(cases, map[string]any{ |
| 500 | "CaseName": st.Name, |
| 501 | "SourceFieldName": Goify(st.Name, true), |
| 502 | "TargetFieldName": Goify(tt.Name, true), |
| 503 | "SourceAttr": st.Attribute, |
| 504 | "TargetAttr": tt.Attribute, |