(ctx context.Context, res *sharedResult, hitCache bool, resolver TypeResolver)
| 1633 | } |
| 1634 | |
| 1635 | func wrapSharedResultWithResolver(ctx context.Context, res *sharedResult, hitCache bool, resolver TypeResolver) (AnyResult, error) { |
| 1636 | ret := Result[Typed]{ |
| 1637 | shared: res, |
| 1638 | hitCache: hitCache, |
| 1639 | } |
| 1640 | if res == nil { |
| 1641 | return ret, nil |
| 1642 | } |
| 1643 | state := res.loadPayloadState() |
| 1644 | if !state.isObject { |
| 1645 | return ret, nil |
| 1646 | } |
| 1647 | typeName := sharedResultObjectTypeName(res, state) |
| 1648 | if state.self == nil { |
| 1649 | switch { |
| 1650 | case state.persistedEnvelope != nil: |
| 1651 | return nil, fmt.Errorf("reconstruct object result %q: persisted payload has not been decoded", typeName) |
| 1652 | case state.hasValue: |
| 1653 | return nil, fmt.Errorf("reconstruct object result %q: invalid payload state (hasValue=true, self=nil)", typeName) |
| 1654 | default: |
| 1655 | return nil, fmt.Errorf("reconstruct object result %q: missing typed payload", typeName) |
| 1656 | } |
| 1657 | } |
| 1658 | if typeName == "" { |
| 1659 | return nil, fmt.Errorf("reconstruct object result: missing type name") |
| 1660 | } |
| 1661 | // Prefer the current resolver's class so cache hits re-wrap against the |
| 1662 | // reading server (which may have its own per-view/per-server resolvers). |
| 1663 | if resolver != nil { |
| 1664 | if objType, ok := resolver.ObjectType(typeName); ok { |
| 1665 | return objType.New(ret) |
| 1666 | } |
| 1667 | } |
| 1668 | // Resolver doesn't know the type — typically a cross-module hit where the |
| 1669 | // concrete type lives in a module not installed in the caller's schema. |
| 1670 | // Reuse the class captured at result construction; it works regardless of |
| 1671 | // where the result is being read from. |
| 1672 | if state.objClass != nil && state.objClass.TypeName() == typeName { |
| 1673 | return state.objClass.New(ret) |
| 1674 | } |
| 1675 | if resolver == nil { |
| 1676 | return nil, fmt.Errorf("reconstruct object result %q: missing type resolver", typeName) |
| 1677 | } |
| 1678 | // Last resort: rebuild a dep-aware resolver from the result's call frame. |
| 1679 | // Reached when class capture missed a path (e.g., a value materialized in |
| 1680 | // core/object.go's ConvertFromSDKResult against a server that doesn't have |
| 1681 | // the producing module installed, or a persisted import loaded by ID |
| 1682 | // before any class-bearing wrap). The resolved class is cached back so |
| 1683 | // subsequent reconstructions skip this branch. |
| 1684 | depResolver, err := resolverForSharedResultObject(ctx, resolver, res, typeName) |
| 1685 | if err != nil { |
| 1686 | return nil, err |
| 1687 | } |
| 1688 | if depResolver != nil { |
| 1689 | if objType, ok := depResolver.ObjectType(typeName); ok { |
| 1690 | objRes, err := objType.New(ret) |
| 1691 | if err != nil { |
| 1692 | return nil, fmt.Errorf("reconstruct object result %q: %w", typeName, err) |
no test coverage detected