Created
July 19, 2024 09:53
-
-
Save robnadin/b2ad61ad3f3d31153ccbf1547b6cf9df to your computer and use it in GitHub Desktop.
Adds dynamic member lookup of captures when matching regular expressions using the RegexBuilder API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import RegexBuilder | |
public struct RegularExpression<Output, Captures> { | |
fileprivate let references = References<Captures>() | |
fileprivate let regex: Regex<Output> | |
public init(for capturesType: Captures.Type = Captures.self, @RegexComponentBuilder _ content: (References<Captures>) -> some RegexComponent<Output>) { | |
let components = content(references) | |
regex = Regex { components } | |
} | |
} | |
extension RegularExpression { | |
@dynamicMemberLookup | |
public struct References<Root> { | |
private let state = OSAllocatedUnfairLock<[PartialKeyPath<Root>: any RegexComponent]>(initialState: [:]) | |
public subscript<Value>(dynamicMember keyPath: KeyPath<Root, Value>) -> Reference<Value> { | |
state.withLock { | |
if let value = $0[keyPath] as? Reference<Value> { | |
return value | |
} | |
let newValue = Reference<Value>() | |
$0[keyPath] = newValue | |
return newValue | |
} | |
} | |
} | |
@dynamicMemberLookup | |
public struct Match { | |
fileprivate let references: References<Captures> | |
fileprivate let match: Regex<Output>.Match | |
public subscript<T>(dynamicMember keyPath: KeyPath<References<Captures>, Reference<T>>) -> T { | |
let reference = references[keyPath: keyPath] | |
return match[reference] | |
} | |
} | |
} | |
extension BidirectionalCollection where SubSequence == Substring { | |
public func firstMatch<Output, Captures>(of regex: RegularExpression<Output, Captures>) -> RegularExpression<Output, Captures>.Match? { | |
guard let match = firstMatch(of: regex.regex) else { | |
return nil | |
} | |
return .init(references: regex.references, match: match) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment