|
| 1 | +import Foundation |
1 | 2 | import HelperCoders |
2 | 3 | import MetaCodable |
3 | 4 | import Testing |
@@ -673,4 +674,132 @@ struct CodedAtEnumTests { |
673 | 674 | ) |
674 | 675 | } |
675 | 676 | } |
| 677 | + |
| 678 | + struct WithOnlyAssociatedVariablesAtTopLevel { |
| 679 | + @Codable |
| 680 | + @CodedAt("type") |
| 681 | + enum TypeObject { |
| 682 | + case type1(Type1) |
| 683 | + |
| 684 | + @Codable |
| 685 | + struct Type1 { |
| 686 | + let int: Int |
| 687 | + } |
| 688 | + } |
| 689 | + |
| 690 | + @Test |
| 691 | + func expansion() throws { |
| 692 | + assertMacroExpansion( |
| 693 | + """ |
| 694 | + @Codable |
| 695 | + @CodedAt("type") |
| 696 | + enum TypeObject { |
| 697 | + case type1(Int) |
| 698 | + } |
| 699 | + """, |
| 700 | + expandedSource: |
| 701 | + """ |
| 702 | + enum TypeObject { |
| 703 | + case type1(Int) |
| 704 | + } |
| 705 | +
|
| 706 | + extension TypeObject: Decodable { |
| 707 | + init(from decoder: any Decoder) throws { |
| 708 | + var typeContainer: KeyedDecodingContainer<CodingKeys>? |
| 709 | + let container = try? decoder.container(keyedBy: CodingKeys.self) |
| 710 | + if let container = container { |
| 711 | + typeContainer = container |
| 712 | + } else { |
| 713 | + typeContainer = nil |
| 714 | + } |
| 715 | + if let typeContainer = typeContainer { |
| 716 | + let typeString: String? |
| 717 | + do { |
| 718 | + typeString = try typeContainer.decodeIfPresent(String.self, forKey: CodingKeys.type) ?? nil |
| 719 | + } catch { |
| 720 | + typeString = nil |
| 721 | + } |
| 722 | + if let typeString = typeString { |
| 723 | + switch typeString { |
| 724 | + case "type1": |
| 725 | + let _0: Int |
| 726 | + _0 = try Int(from: decoder) |
| 727 | + self = .type1(_0) |
| 728 | + return |
| 729 | + default: |
| 730 | + break |
| 731 | + } |
| 732 | + } |
| 733 | + } |
| 734 | + let context = DecodingError.Context( |
| 735 | + codingPath: decoder.codingPath, |
| 736 | + debugDescription: "Couldn't match any cases." |
| 737 | + ) |
| 738 | + throw DecodingError.typeMismatch(Self.self, context) |
| 739 | + } |
| 740 | + } |
| 741 | +
|
| 742 | + extension TypeObject: Encodable { |
| 743 | + func encode(to encoder: any Encoder) throws { |
| 744 | + let container = encoder.container(keyedBy: CodingKeys.self) |
| 745 | + var typeContainer = container |
| 746 | + switch self { |
| 747 | + case .type1(let _0): |
| 748 | + try typeContainer.encode("type1", forKey: CodingKeys.type) |
| 749 | + try _0.encode(to: encoder) |
| 750 | + } |
| 751 | + } |
| 752 | + } |
| 753 | +
|
| 754 | + extension TypeObject { |
| 755 | + enum CodingKeys: String, CodingKey { |
| 756 | + case type = "type" |
| 757 | + } |
| 758 | + } |
| 759 | + """ |
| 760 | + ) |
| 761 | + } |
| 762 | + |
| 763 | + @Test |
| 764 | + func decodingAndEncoding() throws { |
| 765 | + let original = TypeObject.type1(TypeObject.Type1(int: 42)) |
| 766 | + let encoded = try JSONEncoder().encode(original) |
| 767 | + let decoded = try JSONDecoder().decode( |
| 768 | + TypeObject.self, from: encoded) |
| 769 | + if case .type1(let data) = decoded { |
| 770 | + #expect(data.int == 42) |
| 771 | + } else { |
| 772 | + Issue.record("Expected type1 case") |
| 773 | + } |
| 774 | + } |
| 775 | + |
| 776 | + @Test |
| 777 | + func decodingFromJSON() throws { |
| 778 | + let jsonStr = """ |
| 779 | + { |
| 780 | + "type": "type1", |
| 781 | + "int": 42 |
| 782 | + } |
| 783 | + """ |
| 784 | + let jsonData = try #require(jsonStr.data(using: .utf8)) |
| 785 | + let decoded = try JSONDecoder().decode( |
| 786 | + TypeObject.self, from: jsonData) |
| 787 | + if case .type1(let data) = decoded { |
| 788 | + #expect(data.int == 42) |
| 789 | + } else { |
| 790 | + Issue.record("Expected type1 case") |
| 791 | + } |
| 792 | + } |
| 793 | + |
| 794 | + @Test |
| 795 | + func encodingToJSON() throws { |
| 796 | + let original = TypeObject.type1(TypeObject.Type1(int: 42)) |
| 797 | + let encoded = try JSONEncoder().encode(original) |
| 798 | + let json = |
| 799 | + try JSONSerialization.jsonObject(with: encoded) |
| 800 | + as! [String: Any] |
| 801 | + #expect(json["type"] as? String == "type1") |
| 802 | + #expect(json["int"] as? Int == 42) |
| 803 | + } |
| 804 | + } |
676 | 805 | } |
0 commit comments