99//
1010//===----------------------------------------------------------------------===//
1111
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.
1415///
1516/// Access the namespace via the `.overlay` member, available on all collections:
1617///
2425///
2526public struct OverlayCollectionNamespace < Elements: Collection > {
2627
27- @usableFromInline
28- internal var elements : Elements
28+ public let elements : Elements
2929
3030 @inlinable
3131 internal init ( elements: Elements ) {
@@ -35,13 +35,44 @@ public struct OverlayCollectionNamespace<Elements: Collection> {
3535
3636extension Collection {
3737
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.
4041 ///
4142 @inlinable
4243 public var overlay : OverlayCollectionNamespace < Self > {
4344 OverlayCollectionNamespace ( elements: self )
4445 }
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+ }
4576}
4677
4778extension OverlayCollectionNamespace {
@@ -96,20 +127,34 @@ extension OverlayCollectionNamespace {
96127 }
97128}
98129
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+ ///
99144public struct OverlayCollection < Base, Overlay>
100145where Base: Collection , Overlay: Collection , Base. Element == Overlay . Element {
101146
102147 @usableFromInline
103148 internal var base : Base
104149
105150 @usableFromInline
106- internal var overlay : Overlay
151+ internal var overlay : Optional < Overlay >
107152
108153 @usableFromInline
109154 internal var replacedRange : Range < Base . Index >
110155
111156 @inlinable
112- internal init ( base: Base , overlay: Overlay , replacedRange: Range < Base . Index > ) {
157+ internal init ( base: Base , overlay: Overlay ? , replacedRange: Range < Base . Index > ) {
113158 self . base = base
114159 self . overlay = overlay
115160 self . replacedRange = replacedRange
@@ -187,7 +232,7 @@ extension OverlayCollection {
187232
188233 @inlinable
189234 public var startIndex : Index {
190- if base. startIndex == replacedRange. lowerBound {
235+ if let overlay = overlay , base. startIndex == replacedRange. lowerBound {
191236 if overlay. isEmpty {
192237 return makeIndex ( replacedRange. upperBound)
193238 }
@@ -198,6 +243,9 @@ extension OverlayCollection {
198243
199244 @inlinable
200245 public var endIndex : Index {
246+ guard let overlay = overlay else {
247+ return makeIndex ( base. endIndex)
248+ }
201249 if replacedRange. lowerBound != base. endIndex || overlay. isEmpty {
202250 return makeIndex ( base. endIndex)
203251 }
@@ -206,7 +254,10 @@ extension OverlayCollection {
206254
207255 @inlinable
208256 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)
210261 + overlay. count
211262 + base. distance ( from: replacedRange. upperBound, to: base. endIndex)
212263 }
@@ -216,7 +267,7 @@ extension OverlayCollection {
216267 switch i. wrapped {
217268 case . base( var baseIndex) :
218269 base. formIndex ( after: & baseIndex)
219- if baseIndex == replacedRange. lowerBound {
270+ if let overlay = overlay , baseIndex == replacedRange. lowerBound {
220271 if overlay. isEmpty {
221272 return makeIndex ( replacedRange. upperBound)
222273 }
@@ -225,8 +276,8 @@ extension OverlayCollection {
225276 return makeIndex ( baseIndex)
226277
227278 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 {
230281 return makeIndex ( replacedRange. upperBound)
231282 }
232283 return makeIndex ( overlayIndex)
@@ -239,7 +290,7 @@ extension OverlayCollection {
239290 case . base( let baseIndex) :
240291 return base [ baseIndex]
241292 case . overlay( let overlayIndex) :
242- return overlay [ overlayIndex]
293+ return overlay! [ overlayIndex]
243294 }
244295 }
245296}
@@ -251,7 +302,7 @@ where Base: BidirectionalCollection, Overlay: BidirectionalCollection {
251302 public func index( before i: Index ) -> Index {
252303 switch i. wrapped {
253304 case . base( var baseIndex) :
254- if baseIndex == replacedRange. upperBound {
305+ if let overlay = overlay , baseIndex == replacedRange. upperBound {
255306 if overlay. isEmpty {
256307 return makeIndex ( base. index ( before: replacedRange. lowerBound) )
257308 }
@@ -261,10 +312,10 @@ where Base: BidirectionalCollection, Overlay: BidirectionalCollection {
261312 return makeIndex ( baseIndex)
262313
263314 case . overlay( var overlayIndex) :
264- if overlayIndex == overlay. startIndex {
315+ if overlayIndex == overlay! . startIndex {
265316 return makeIndex ( base. index ( before: replacedRange. lowerBound) )
266317 }
267- overlay. formIndex ( before: & overlayIndex)
318+ overlay! . formIndex ( before: & overlayIndex)
268319 return makeIndex ( overlayIndex)
269320 }
270321 }
0 commit comments