I've been using Codable enums to encode my classes to JSON, and I've hit a roadblock:
enum MyEnum: Codable {
case case1(data: Data)
case case2(str: String)
case case3(url: URL)
}
// ...
myFunc(myEnumType: MyEnum.case1)
func myFunc(
myEnumType: MyEnum // ?
) -> MyEnum {
switch myEnumType {
case .case1:
// insert processing...
return myEnumType(data: ...)
case .case2:
// ...
return myEnumType(str: ...)
case .case3:
// ...
return myEnumType(url: ...)
}
}
Because MyEnum.case1
is of type function
, I couldn't use MyEnum
, so I had to resort to this very ugly solution[1] [2] [3]:
enum MyEnum: Codable {
case case1(data: Data)
case case2(str: String)
case case3(url: URL)
}
// :(
enum MyEnumType {
case case1
case case2
case case3
}
// ...
func myFunc(
myEnumType: MyEnumType // :(
) -> MyEnum {
How can I pass my case to the function as a parameter?
This is what I don't want:
typealias MyEnumType<Arg1> = (Arg1) -> MyEnum
As this could just be any function returning MyEnum
. This doesn't guarantee it's a case
of MyEnum
.
myFunc(myEnumType: MyEnum.case1(data: dummyData))
I don't want to initialize the case with dummy data. myFunc
is the one that's supposed to initialize the case with actual data, then return the initialized case.
myFunc(myEnumType: MyEnum)
This is irrelevant: I want to know what case
I'm looking at in the function in order to init each enum
uniquely.
Something like:
func myFunc<T>(myEnumType: MyEnum.T) -> MyEnum {
Which ensures the uninitialized case I've passed to the function is indeed a case of the MyEnum
enum.
I'm open to changing the function call or the function, as long as I'm not initializing the case or having to redeclare every case.
I've been using Codable enums to encode my classes to JSON, and I've hit a roadblock:
enum MyEnum: Codable {
case case1(data: Data)
case case2(str: String)
case case3(url: URL)
}
// ...
myFunc(myEnumType: MyEnum.case1)
func myFunc(
myEnumType: MyEnum // ?
) -> MyEnum {
switch myEnumType {
case .case1:
// insert processing...
return myEnumType(data: ...)
case .case2:
// ...
return myEnumType(str: ...)
case .case3:
// ...
return myEnumType(url: ...)
}
}
Because MyEnum.case1
is of type function
, I couldn't use MyEnum
, so I had to resort to this very ugly solution[1] [2] [3]:
enum MyEnum: Codable {
case case1(data: Data)
case case2(str: String)
case case3(url: URL)
}
// :(
enum MyEnumType {
case case1
case case2
case case3
}
// ...
func myFunc(
myEnumType: MyEnumType // :(
) -> MyEnum {
How can I pass my case to the function as a parameter?
This is what I don't want:
typealias MyEnumType<Arg1> = (Arg1) -> MyEnum
As this could just be any function returning MyEnum
. This doesn't guarantee it's a case
of MyEnum
.
myFunc(myEnumType: MyEnum.case1(data: dummyData))
I don't want to initialize the case with dummy data. myFunc
is the one that's supposed to initialize the case with actual data, then return the initialized case.
myFunc(myEnumType: MyEnum)
This is irrelevant: I want to know what case
I'm looking at in the function in order to init each enum
uniquely.
Something like:
func myFunc<T>(myEnumType: MyEnum.T) -> MyEnum {
Which ensures the uninitialized case I've passed to the function is indeed a case of the MyEnum
enum.
I'm open to changing the function call or the function, as long as I'm not initializing the case or having to redeclare every case.
In your "ideal solution", you mention a nested type MyEnum.T
. This can be generated by a macro. Here is an example implementation:
// declaration:
@attached(member, names: named(T))
public macro RawCases() = #externalMacro(module: "...", type: "RawCases")
// implementation:
enum RawCases: MemberMacro {
static func expansion(of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext) throws -> [DeclSyntax] {
let caseNames = declaration.memberBlock.members
.compactMap { $0.decl.as(EnumCaseDeclSyntax.self) }
.flatMap(\.elements)
.map(\.name)
return [DeclSyntax(
try EnumDeclSyntax("enum T") {
for name in caseNames {
"case \(name)"
}
}
)]
}
}
Usage:
@RawCases
enum MyEnum: Codable {
case case1(data: Data)
case case2(str: String)
case case3(url: URL)
}
func f(_ value: MyEnum.T) -> MyEnum {
switch value {
case .case1:
.case1(data: Data([1, 2, 3]))
case .case2:
.case2(str: "Something")
case .case3:
.case3(url: URL(filePath: "/foo/bar"))
}
}
MyEnumType
in your "ugly" solution. Does that make it less ugly? – Sweeper Commented Jan 22 at 23:05