Skip to content

Commit

Permalink
Add tuple extraction
Browse files Browse the repository at this point in the history
  • Loading branch information
griotspeak committed Dec 24, 2024
1 parent 40aade2 commit 65ed73b
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 14 deletions.
11 changes: 9 additions & 2 deletions Sources/DiscriminatedUnion/DiscriminatedUnion.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@

@attached(
extension,
conformances: DiscriminatedUnion
)
@attached(
member,
names: named(Discriminant), named(discriminant)
names: arbitrary
// prefixed(tupleFrom),
// named(Discriminant),
// named(discriminant),
// named(ExtractorError)
)
@attached(extension, conformances: DiscriminatedUnion)
public macro discriminatedUnion() = #externalMacro(
module: "DiscriminatedUnionMacros",
type: "DiscriminatedUnionMacro")
Expand Down
2 changes: 2 additions & 0 deletions Sources/DiscriminatedUnionClient/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ enum Pet {
case parrot(loud: Bool)
case snake
case turtle(snapper: Bool)
// case bird(name: String, Int)

}

Swift.print("usiyan::: Pet.Discriminant.dog == Pet.dog.discriminant: \(String(describing: Pet.Discriminant.dog == Pet.dog.discriminant))")
85 changes: 74 additions & 11 deletions Sources/DiscriminatedUnionMacros/DiscriminatedUnionMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ extension DiscriminatedUnionMacro: MemberMacro {
public static func expansion<Declaration, Context>(
of node: SwiftSyntax.AttributeSyntax,
providingMembersOf declaration: Declaration,
conformingTo protocols: [TypeSyntax],
in context: Context
) throws -> [SwiftSyntax.DeclSyntax]
where
Expand All @@ -53,19 +54,34 @@ extension DiscriminatedUnionMacro: MemberMacro {

let unvalidatedPropertyDecl = try instance.declareDiscriminantProperty()
let validatedPropertyDecl = try DeclSyntax(validating: unvalidatedPropertyDecl)
let validatedExtractors = try instance.doSomethingSpecial().map {
try DeclSyntax(validating: $0)
}

let validatedExtractorError = try DeclSyntax(validating: extractorErrorDecl())

return try [
DeclSyntax(validating: "\(raw: discriminantDecl)"),
validatedPropertyDecl
]
validatedPropertyDecl,
validatedExtractorError
] + validatedExtractors
}

enum Error: Swift.Error {
case attemptPrint(String)
}

static func extractorErrorDecl() -> DeclSyntax {
"""
public enum ExtractorError: Swift.Error {
case invalidExtraction(expected: Discriminant, actual: Discriminant)
}
"""
}

func declareDiscriminantType() throws -> EnumDeclSyntax {
try EnumDeclSyntax("public enum Discriminant: DiscriminantType") {
return try EnumDeclSyntax("public enum Discriminant: DiscriminantType") {

for singleCase in childCases {
EnumCaseDeclSyntax(
leadingTrivia: .newline) {
Expand All @@ -75,25 +91,24 @@ extension DiscriminatedUnionMacro: MemberMacro {
}
}

"\n"
"\n"

try declareAssociatedTypeFunction()
try declareHasAssociatedTypeFunction()
}
}

func declareAssociatedTypeFunction() throws -> DeclSyntax {
func declareHasAssociatedTypeFunction() throws -> DeclSyntax {
let theCases = childCases.map { singleCase in
let myTrivia = singleCase.parameterClause?.parameters.description
return "case .\(singleCase.name): \(singleCase.parameterClause != nil) // \(String(describing: myTrivia))"
}
let theSwitch = """
switch self {
return switch self {
\(theCases.joined(separator: "\n"))
}
"""
return DeclSyntax(stringLiteral:"""
public var hasAssociatedType: Bool {
\(theSwitch)
Expand All @@ -102,6 +117,54 @@ extension DiscriminatedUnionMacro: MemberMacro {
)
}

func doSomethingSpecial() throws -> [DeclSyntax] {
let theCases = childCases.compactMap { singleCase in
if let parameterClause = singleCase.parameterClause {
let bindings = parameterClause.parameters.enumerated().map({ (index, parameter) in
"let \(parameter.firstName ?? "index\(raw: index)")"
})

let rawOut = parameterClause.parameters.enumerated().map({ (index, parameter) in
"\(parameter.firstName ?? "index\(raw: index)")"
})

let output: String
if parameterClause.parameters.count == 1 {
output = rawOut.first!
} else {
output = "(\(rawOut.joined(separator: ", ")))"
}

return (
String(describing: singleCase.name),
parameterClause.parameters.count == 1 ? String(describing: parameterClause.parameters.first!.type) : "(\(parameterClause.parameters.description))",
bindings.joined(separator: ", "),
output
)
} else {
return nil
}
}

let theSomethings: [DeclSyntax] = theCases.map { caseName, tupleType, pBindings, returnValue in
let titleCasedName = "\(caseName.first!.uppercased())\(caseName.dropFirst())"
return """
public func tupleFrom\(raw: titleCasedName)() throws -> \(raw: tupleType) {
if case .\(raw: caseName)(\(raw: pBindings)) = self {
return \(raw: returnValue)
} else {
throw ExtractorError.invalidExtraction(expected: .\(raw: caseName), actual: self.discriminant)
}
}
"""
}

return theSomethings
}


func declareDiscriminantProperty() throws -> DeclSyntax {
let casesWrittenOut = childCases.map {
"""
Expand All @@ -110,17 +173,17 @@ extension DiscriminatedUnionMacro: MemberMacro {
"""
}.joined(separator: "\n")

let switchWrittenOut: DeclSyntax =
let switchWrittenOut: String =
"""
switch self {
\(raw: casesWrittenOut)
\(casesWrittenOut)
}
"""

return
"""
public var discriminant: Discriminant {
\(switchWrittenOut)
\(raw: switchWrittenOut)
}
"""

Expand Down
29 changes: 28 additions & 1 deletion Tests/DiscriminatedUnionTests/DiscriminatedUnionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ final class DiscriminatedUnionTests: XCTestCase {
case cat(curious: Bool)
case parrot
case snake
case bird(name: String, Int)
}
""",

Expand All @@ -33,15 +34,17 @@ enum Pet {
case cat(curious: Bool)
case parrot
case snake
case bird(name: String, Int)
public enum Discriminant: DiscriminantType {
case dog
case cat
case parrot
case snake
case bird
public var hasAssociatedType: Bool {
switch self {
return switch self {
case .dog:
false // nil
case .cat:
Expand All @@ -50,6 +53,8 @@ enum Pet {
false // nil
case .snake:
false // nil
case .bird:
true // Optional("name: String, Int")
}
}
}
Expand All @@ -64,6 +69,28 @@ enum Pet {
return .parrot
case .snake:
return .snake
case .bird:
return .bird
}
}
public enum ExtractorError: Swift.Error {
case invalidExtraction(expected: Discriminant, actual: Discriminant)
}
public func catTupleValue() throws -> Bool {
if case .cat(let curious) = self {
return curious
} else {
throw ExtractorError.invalidExtraction(expected: .cat, actual: self.discriminant)
}
}
public func birdTupleValue() throws -> (name: String, Int) {
if case .bird(let name, let index1) = self {
return (name, index1)
} else {
throw ExtractorError.invalidExtraction(expected: .bird, actual: self.discriminant)
}
}
}
Expand Down

0 comments on commit 65ed73b

Please sign in to comment.