9
9
//
10
10
//===----------------------------------------------------------------------===//
11
11
12
- /// A namespace for methods which overlay a collection of elements
13
- /// over a region of a base collection.
12
+ /// A namespace for methods which return composed collections,
13
+ /// formed by replacing a region of a base collection
14
+ /// with another collection of elements.
14
15
///
15
16
/// Access the namespace via the `.overlay` member, available on all collections:
16
17
///
24
25
///
25
26
public struct OverlayCollectionNamespace < Elements: Collection > {
26
27
27
- @usableFromInline
28
- internal var elements : Elements
28
+ public let elements : Elements
29
29
30
30
@inlinable
31
31
internal init ( elements: Elements ) {
@@ -35,13 +35,44 @@ public struct OverlayCollectionNamespace<Elements: Collection> {
35
35
36
36
extension Collection {
37
37
38
- /// A namespace for methods which overlay another collection of elements
39
- /// over a region of this collection.
38
+ /// A namespace for methods which return composed collections,
39
+ /// formed by replacing a region of this collection
40
+ /// with another collection of elements.
40
41
///
41
42
@inlinable
42
43
public var overlay : OverlayCollectionNamespace < Self > {
43
44
OverlayCollectionNamespace ( elements: self )
44
45
}
46
+
47
+ /// If `condition` is true, returns an `OverlayCollection` by applying the given closure.
48
+ /// Otherwise, returns an `OverlayCollection` containing the same elements as this collection.
49
+ ///
50
+ /// The following example takes an array of products, lazily wraps them in a `ListItem` enum,
51
+ /// and conditionally inserts a call-to-action element if `showCallToAction` is true.
52
+ ///
53
+ /// ```swift
54
+ /// var listItems: some Collection<ListItem> {
55
+ /// let products: [Product] = ...
56
+ /// return products
57
+ /// .lazy.map {
58
+ /// ListItem.product($0)
59
+ /// }
60
+ /// .overlay(if: showCallToAction) {
61
+ /// $0.inserting(.callToAction, at: min(4, $0.elements.count))
62
+ /// }
63
+ /// }
64
+ /// ```
65
+ ///
66
+ @inlinable
67
+ public func overlay< Overlay> (
68
+ if condition: Bool , _ makeOverlay: ( OverlayCollectionNamespace < Self > ) -> OverlayCollection < Self , Overlay >
69
+ ) -> OverlayCollection < Self , Overlay > {
70
+ if condition {
71
+ return makeOverlay ( overlay)
72
+ } else {
73
+ return OverlayCollection ( base: self , overlay: nil , replacedRange: startIndex..< startIndex)
74
+ }
75
+ }
45
76
}
46
77
47
78
extension OverlayCollectionNamespace {
@@ -96,20 +127,34 @@ extension OverlayCollectionNamespace {
96
127
}
97
128
}
98
129
130
+ /// A composed collections, formed by replacing a region of a base collection
131
+ /// with another collection of elements.
132
+ ///
133
+ /// To create an OverlayCollection, use the methods in the ``OverlayCollectionNamespace``
134
+ /// namespace:
135
+ ///
136
+ /// ```swift
137
+ /// let base = 0..<5
138
+ /// for n in base.overlay.inserting(42, at: 2) {
139
+ /// print(n)
140
+ /// }
141
+ /// // Prints: 0, 1, 42, 2, 3, 4
142
+ /// ```
143
+ ///
99
144
public struct OverlayCollection < Base, Overlay>
100
145
where Base: Collection , Overlay: Collection , Base. Element == Overlay . Element {
101
146
102
147
@usableFromInline
103
148
internal var base : Base
104
149
105
150
@usableFromInline
106
- internal var overlay : Overlay
151
+ internal var overlay : Optional < Overlay >
107
152
108
153
@usableFromInline
109
154
internal var replacedRange : Range < Base . Index >
110
155
111
156
@inlinable
112
- internal init ( base: Base , overlay: Overlay , replacedRange: Range < Base . Index > ) {
157
+ internal init ( base: Base , overlay: Overlay ? , replacedRange: Range < Base . Index > ) {
113
158
self . base = base
114
159
self . overlay = overlay
115
160
self . replacedRange = replacedRange
@@ -187,7 +232,7 @@ extension OverlayCollection {
187
232
188
233
@inlinable
189
234
public var startIndex : Index {
190
- if base. startIndex == replacedRange. lowerBound {
235
+ if let overlay = overlay , base. startIndex == replacedRange. lowerBound {
191
236
if overlay. isEmpty {
192
237
return makeIndex ( replacedRange. upperBound)
193
238
}
@@ -198,6 +243,9 @@ extension OverlayCollection {
198
243
199
244
@inlinable
200
245
public var endIndex : Index {
246
+ guard let overlay = overlay else {
247
+ return makeIndex ( base. endIndex)
248
+ }
201
249
if replacedRange. lowerBound != base. endIndex || overlay. isEmpty {
202
250
return makeIndex ( base. endIndex)
203
251
}
@@ -206,7 +254,10 @@ extension OverlayCollection {
206
254
207
255
@inlinable
208
256
public var count : Int {
209
- base. distance ( from: base. startIndex, to: replacedRange. lowerBound)
257
+ guard let overlay = overlay else {
258
+ return base. count
259
+ }
260
+ return base. distance ( from: base. startIndex, to: replacedRange. lowerBound)
210
261
+ overlay. count
211
262
+ base. distance ( from: replacedRange. upperBound, to: base. endIndex)
212
263
}
@@ -216,7 +267,7 @@ extension OverlayCollection {
216
267
switch i. wrapped {
217
268
case . base( var baseIndex) :
218
269
base. formIndex ( after: & baseIndex)
219
- if baseIndex == replacedRange. lowerBound {
270
+ if let overlay = overlay , baseIndex == replacedRange. lowerBound {
220
271
if overlay. isEmpty {
221
272
return makeIndex ( replacedRange. upperBound)
222
273
}
@@ -225,8 +276,8 @@ extension OverlayCollection {
225
276
return makeIndex ( baseIndex)
226
277
227
278
case . overlay( var overlayIndex) :
228
- overlay. formIndex ( after: & overlayIndex)
229
- if replacedRange. lowerBound != base. endIndex, overlayIndex == overlay. endIndex {
279
+ overlay! . formIndex ( after: & overlayIndex)
280
+ if replacedRange. lowerBound != base. endIndex, overlayIndex == overlay! . endIndex {
230
281
return makeIndex ( replacedRange. upperBound)
231
282
}
232
283
return makeIndex ( overlayIndex)
@@ -239,7 +290,7 @@ extension OverlayCollection {
239
290
case . base( let baseIndex) :
240
291
return base [ baseIndex]
241
292
case . overlay( let overlayIndex) :
242
- return overlay [ overlayIndex]
293
+ return overlay! [ overlayIndex]
243
294
}
244
295
}
245
296
}
@@ -251,7 +302,7 @@ where Base: BidirectionalCollection, Overlay: BidirectionalCollection {
251
302
public func index( before i: Index ) -> Index {
252
303
switch i. wrapped {
253
304
case . base( var baseIndex) :
254
- if baseIndex == replacedRange. upperBound {
305
+ if let overlay = overlay , baseIndex == replacedRange. upperBound {
255
306
if overlay. isEmpty {
256
307
return makeIndex ( base. index ( before: replacedRange. lowerBound) )
257
308
}
@@ -261,10 +312,10 @@ where Base: BidirectionalCollection, Overlay: BidirectionalCollection {
261
312
return makeIndex ( baseIndex)
262
313
263
314
case . overlay( var overlayIndex) :
264
- if overlayIndex == overlay. startIndex {
315
+ if overlayIndex == overlay! . startIndex {
265
316
return makeIndex ( base. index ( before: replacedRange. lowerBound) )
266
317
}
267
- overlay. formIndex ( before: & overlayIndex)
318
+ overlay! . formIndex ( before: & overlayIndex)
268
319
return makeIndex ( overlayIndex)
269
320
}
270
321
}
0 commit comments