Skip to content

Add case path iteration #159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 6, 2024
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
config:
- debug
#- release
swift: ['5.9.1']
swift: ['5.10']
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
Expand Down
6 changes: 6 additions & 0 deletions Sources/CasePaths/Never+CasePathable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ extension Case {
return Case<Never>(embed: absurd, extract: { (_: Value) in nil })
}
}

extension Never.AllCasePaths: Sequence {
public func makeIterator() -> some IteratorProtocol<PartialCaseKeyPath<Never>> {
[].makeIterator()
}
}
11 changes: 11 additions & 0 deletions Sources/CasePaths/Optional+CasePathable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ extension Optional: CasePathable {
}
)
}

private let _allCasePaths: [PartialCaseKeyPath<Optional>] = [
\.none,
\.some,
]
}

public static var allCasePaths: AllCasePaths {
Expand All @@ -67,3 +72,9 @@ extension Case {
self[dynamicMember: keyPath].some
}
}

extension Optional.AllCasePaths: Sequence {
public func makeIterator() -> some IteratorProtocol<PartialCaseKeyPath<Optional>> {
_allCasePaths.makeIterator()
}
}
11 changes: 11 additions & 0 deletions Sources/CasePaths/Result+CasePathable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,20 @@ extension Result: CasePathable {
}
)
}

private let _allCasePaths: [PartialCaseKeyPath<Result>] = [
\.success,
\.failure,
]
}

public static var allCasePaths: AllCasePaths {
AllCasePaths()
}
}

extension Result.AllCasePaths: Sequence {
public func makeIterator() -> some IteratorProtocol<PartialCaseKeyPath<Result>> {
_allCasePaths.makeIterator()
}
}
38 changes: 25 additions & 13 deletions Sources/CasePathsMacros/CasePathableMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,35 +83,47 @@ extension CasePathableMacro: MemberMacro {

let rewriter = SelfRewriter(selfEquivalent: enumName)
let memberBlock = rewriter.rewrite(enumDecl.memberBlock).cast(MemberBlockSyntax.self)

let caseSubscript = generateCaseSubscript(from: memberBlock.members, enumName: enumName)

let rootSwitchCases = generateCases(from: memberBlock.members, enumName: enumName) {
"case .\($0.name): return \\.\($0.name)"
}
let rootSwitch: DeclSyntax = rootSwitchCases.isEmpty
? "\\.never"
: """
switch root {
\(raw: rootSwitchCases.map(\.description).joined(separator: "\n"))
}
"""
let casePaths = generateDeclSyntax(from: memberBlock.members, enumName: enumName)
let allCases = generateCases(from: memberBlock.members, enumName: enumName) {
"allCasePaths.append(\\.\($0.name))"
}

return [
"""
public struct AllCasePaths {
public struct AllCasePaths: Sequence {
public subscript(root: \(enumName)) -> PartialCaseKeyPath<\(enumName)> {
switch root {
\(raw: caseSubscript.map(\.description).joined(separator: "\n"))
}
\(rootSwitch)
}
\(raw: casePaths.map(\.description).joined(separator: "\n"))
public func makeIterator() -> IndexingIterator<[PartialCaseKeyPath<\(enumName)>]> {
\(raw: allCases.isEmpty ? "let" : "var") allCasePaths: [PartialCaseKeyPath<\(enumName)>] = []\
\(raw: allCases.map { "\n\($0.description)" }.joined())
return allCasePaths.makeIterator()
}
}
public static var allCasePaths: AllCasePaths { AllCasePaths() }
"""
]
}

static func generateCaseSubscript(
static func generateCases(
from elements: MemberBlockItemListSyntax,
enumName: TokenSyntax
enumName: TokenSyntax,
body: (EnumCaseElementSyntax) -> DeclSyntax
) -> [DeclSyntax] {
elements.flatMap {
if let decl = $0.decl.as(EnumCaseDeclSyntax.self) {
return decl.elements.map {
"case .\($0.name): return \\.\($0.name)" as DeclSyntax
}
return decl.elements.map(body)
}
if let ifConfigDecl = $0.decl.as(IfConfigDeclSyntax.self) {
let ifClauses = ifConfigDecl.clauses.flatMap { decl -> [DeclSyntax] in
Expand All @@ -120,7 +132,7 @@ extension CasePathableMacro: MemberMacro {
}
let title = "\(decl.poundKeyword.text) \(decl.condition?.description ?? "")"
return ["\(raw: title)"]
+ generateCaseSubscript(from: elements, enumName: enumName)
+ generateCases(from: elements, enumName: enumName, body: body)
}
return ifClauses + ["#endif"]
}
Expand Down
Loading