SwiftyLab / MetaCodable

Supercharge Swift's Codable implementations with macros meta-programming.
https://swiftpackageindex.com/SwiftyLab/MetaCodable/main/documentation/metacodable
MIT License
604 stars 22 forks source link

`@CodedAs` fails with same keys in different casing #62

Closed leoMehlig closed 7 months ago

leoMehlig commented 7 months ago

I've noticed a bug with @Codedas when providing multiple keys, which are the same exept for their cases (e.g. camel case and snake case). The macro will expand them to be the same in the CodingKeys enum, which will cause an Invalid redeclaration of 'key' error. See this example:

@Codable
struct TestCodable {
    @CodedAs("fooBar", "foo_bar")
    var fooBar: String
}

This will expand into this code:

extension TestCodable: Decodable {
    init(from decoder: any Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let fooBarKeys = [CodingKeys.fooBar, CodingKeys.fooBar, CodingKeys.fooBar].filter {
            container.allKeys.contains($0)
        }
        guard fooBarKeys.count == 1 else {
            let context = DecodingError.Context(
                codingPath: container.codingPath,
                debugDescription: "Invalid number of keys found, expected one."
            )
            throw DecodingError.typeMismatch(Self.self, context)
        }
        self.fooBar = try container.decode(String.self, forKey: fooBarKeys[0])
    }
}

extension TestCodable: Encodable {
    func encode(to encoder: any Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.fooBar, forKey: CodingKeys.fooBar)
    }
}

extension TestCodable {
    enum CodingKeys: String, CodingKey {
        case fooBar = "fooBar"
        case fooBar = "foo_bar" // << see duplicate key here
    }
}

Thanks a lot for this package. This feels like the missing piece in Codable!