Skip to content

Commit 34e6f40

Browse files
authored
Merge pull request #2 from JohnSundell/ranges
Pass ranges of matches to custom Matcher implementations
2 parents 5719ade + 0047bbb commit 34e6f40

File tree

3 files changed

+41
-32
lines changed

3 files changed

+41
-32
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ var urls = [URL]()
7777
var titles = [String]()
7878

7979
text.scan(using: [
80-
Matcher(identifiers: ["url: "], terminators: ["\n", .end]) {
81-
let string = String($0)
80+
Matcher(identifiers: ["url: "], terminators: ["\n", .end]) { match, range in
81+
let string = String(match)
8282
let url = URL(string: string)
8383
url.flatMap { urls.append($0) }
8484
},
85-
Matcher(identifiers: ["title: "], terminators: ["\n", .end]) {
86-
let string = String($0)
85+
Matcher(identifiers: ["title: "], terminators: ["\n", .end]) { match, range in
86+
let string = String(match)
8787
titles.append(string)
8888
}
8989
])

Sources/Sweep/Sweep.swift

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ extension Terminator: ExpressibleByStringLiteral {
8080
/// which gets all substrings that appear between a set of
8181
/// identifiers and terminators passed to its handler.
8282
public struct Matcher {
83+
/// Closure type used to define a handler for a matcher. When
84+
/// a match is found, the handler is passed the substring that
85+
/// was matched, as well as the range containing the match plus
86+
/// the identifier and terminator that the match is located between.
87+
public typealias Handler = (Substring, ClosedRange<String.Index>) -> Void
88+
8389
/// The identifiers to look for when scanning. When any
8490
/// of the identifiers are found, a matching session begins.
8591
public var identifiers: [Identifier]
@@ -91,13 +97,13 @@ public struct Matcher {
9197
/// The handler to be called when a match was found. A match
9298
/// is considered found when a substring appears between any
9399
/// of the matcher's identifiers and its terminators.
94-
public var handler: (Substring) -> Void
100+
public var handler: Handler
95101

96102
/// Create a new matcher with the desired parameters. See
97103
/// the documentation for each property for more information.
98104
public init(identifiers: [Identifier],
99105
terminators: [Terminator],
100-
handler: @escaping (Substring) -> Void) {
106+
handler: @escaping Handler) {
101107
self.identifiers = identifiers
102108
self.terminators = terminators
103109
self.handler = handler
@@ -109,7 +115,7 @@ public extension Matcher {
109115
/// identifier and terminator, rather than arrays of them.
110116
init(identifier: Identifier,
111117
terminator: Terminator,
112-
handler: @escaping (Substring) -> Void) {
118+
handler: @escaping Handler) {
113119
self.init(identifiers: [identifier],
114120
terminators: [terminator],
115121
handler: handler)
@@ -134,7 +140,9 @@ public extension StringProtocol where SubSequence == Substring {
134140
scan(using: [Matcher(
135141
identifiers: identifiers,
136142
terminators: terminators,
137-
handler: { matches.append($0) }
143+
handler: { match, _ in
144+
matches.append(match)
145+
}
138146
)])
139147

140148
return matches
@@ -143,16 +151,13 @@ public extension StringProtocol where SubSequence == Substring {
143151
/// Scan this string using a custom array of matchers, defined using
144152
/// the `Matcher` type, which gets to handle all matched substrings.
145153
func scan(using matchers: [Matcher]) {
146-
typealias Session = (matcher: Matcher, range: ClosedRange<Index>)
147-
148-
var activeSessions = [Session]()
149-
var partialSessions = [Session]()
154+
var activeSessions = [(Matcher, Identifier, ClosedRange<Index>)]()
155+
var partialSessions = [(Matcher, ClosedRange<Index>)]()
150156
var idleMatchers = matchers
151157

152158
for index in indices {
153-
activeSessions = activeSessions.compactMap {
154-
let matcher = $0.matcher
155-
let range = $0.range.lowerBound...index
159+
activeSessions = activeSessions.compactMap { matcher, identifier, range in
160+
let range = range.lowerBound...index
156161
let match = self[range]
157162

158163
for terminator in matcher.terminators {
@@ -170,20 +175,21 @@ public extension StringProtocol where SubSequence == Substring {
170175
offsetBy: -terminator.string.count)
171176

172177
if endIndex >= range.lowerBound {
173-
let range = range.lowerBound...endIndex
174-
matcher.handler(self[range])
178+
let match = self[range.lowerBound...endIndex]
179+
let identifierLength = identifier.string.count
180+
let lowerBound = self.index(range.lowerBound, offsetBy: -identifierLength)
181+
matcher.handler(match, lowerBound...range.upperBound)
175182
}
176183

177184
idleMatchers.append(matcher)
178185
return nil
179186
}
180187

181-
return (matcher, range)
188+
return (matcher, identifier, range)
182189
}
183190

184-
partialSessions = partialSessions.compactMap {
185-
let matcher = $0.matcher
186-
let range = $0.range.lowerBound...index
191+
partialSessions = partialSessions.compactMap { matcher, range in
192+
let range = range.lowerBound...index
187193
let match = self[range]
188194

189195
for identifier in matcher.identifiers {
@@ -197,7 +203,7 @@ public extension StringProtocol where SubSequence == Substring {
197203

198204
let nextIndex = self.index(after: index)
199205
let range = nextIndex...nextIndex
200-
activeSessions.append((matcher, range))
206+
activeSessions.append((matcher, identifier, range))
201207
return nil
202208
}
203209

@@ -216,7 +222,8 @@ public extension StringProtocol where SubSequence == Substring {
216222
if identifier.string.first == self[index] {
217223
if identifier.string.count == 1 {
218224
let nextIndex = self.index(after: index)
219-
activeSessions.append((matcher, nextIndex...nextIndex))
225+
let range = nextIndex...nextIndex
226+
activeSessions.append((matcher, identifier, range))
220227
} else {
221228
partialSessions.append((matcher, index...index))
222229
}

Tests/SweepTests/SweepTests.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,23 +99,25 @@ final class SweepTests: XCTestCase {
9999
}
100100

101101
func testMultipleMatchers() {
102-
let string = "Some text <First> some other text [Second]."
103-
var matches = (
104-
a: [Substring](),
105-
b: [Substring]()
106-
)
102+
let string = "Some text <First> some other text [[Second]]."
103+
var matches = (a: [Substring](), b: [Substring]())
104+
var ranges = (a: [ClosedRange<String.Index>](), b: [ClosedRange<String.Index>]())
107105

108106
string.scan(using: [
109-
Matcher(identifier: "<", terminator: ">") {
110-
matches.a.append($0)
107+
Matcher(identifier: "<", terminator: ">") { match, range in
108+
matches.a.append(match)
109+
ranges.a.append(range)
111110
},
112-
Matcher(identifier: "[", terminator: "]") {
113-
matches.b.append($0)
111+
Matcher(identifier: "[[", terminator: "]]") { match, range in
112+
matches.b.append(match)
113+
ranges.b.append(range)
114114
}
115115
])
116116

117117
XCTAssertEqual(matches.a, ["First"])
118+
XCTAssertEqual(ranges.a.map { string[$0] }, ["<First>"])
118119
XCTAssertEqual(matches.b, ["Second"])
120+
XCTAssertEqual(ranges.b.map { string[$0] }, ["[[Second]]"])
119121
}
120122

121123
func testAllTestsRunOnLinux() {

0 commit comments

Comments
 (0)