Skip to content

Commit fef8e15

Browse files
authored
Merge pull request #23 from RougeWare/feature/index-traversal
Introduced `indexOrNil` & `formIndexOrNil`
2 parents 9ed34e4 + ec0e9bd commit fef8e15

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

Sources/SafeCollectionAccess/Safe Collection Access.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,79 @@ public extension RandomAccessCollection {
167167
}
168168
}
169169
}
170+
171+
172+
173+
// MARK: - `nil` or `index(before:)`/`index(after:)`
174+
175+
public extension BidirectionalCollection {
176+
177+
/// Returns the position immediately after the given index, or `nil` if there is none.
178+
///
179+
/// The successor of an index must be well defined. For an index `i` into an immutable collection `c`, calling `c.index(after: i)` returns the same index every time.
180+
///
181+
/// - Parameter i: An index of the collection.
182+
/// - Returns: The index value immediately after `i`, or `nil` if such an index wouldn't be in the collection.
183+
func indexOrNil(after i: Index) -> Index? {
184+
guard i < endIndex else { return nil }
185+
return index(after: i)
186+
}
187+
188+
189+
/// Replaces the given index with its successor, or `nil` if there is none.
190+
///
191+
/// If you pass `nil` for `i`, this returns immediately because there's nothing after nothing.
192+
/// If the resulting index wouldn't be in the collection, then `i` is set to `nil`.
193+
///
194+
/// - Parameter i: An index of the collection.
195+
func formIndexOrNil(after i: inout Index?) {
196+
guard let nonNilIndex = i else { return }
197+
i = indexOrNil(after: nonNilIndex)
198+
}
199+
200+
201+
/// Returns the position immediately before the given index, or `nil` if there is none.
202+
///
203+
/// - Parameter i: An index of the collection.
204+
/// - Returns: The index value immediately before `i`, or `nil` if such an index wouldn't be in the collection.
205+
func indexOrNil(before i: Index) -> Index? {
206+
guard i > startIndex else { return nil }
207+
return index(before: i)
208+
}
209+
210+
211+
/// Replaces the given index with its predecessor, or `nil` if there is none.
212+
///
213+
/// If you pass `nil` for `i`, this returns immediately because there's nothing before nothing.
214+
/// If the resulting index wouldn't be in the collection, then `i` is set to `nil`.
215+
///
216+
/// - Parameter i: An index of the collection.
217+
func formIndexOrNil(before i: inout Index?) {
218+
guard let nonNilIndex = i else { return }
219+
i = indexOrNil(before: nonNilIndex)
220+
}
221+
222+
223+
/// Returns an index that is the specified distance from the given index, or `nil` if there isn't one.
224+
///
225+
/// - Parameters:
226+
/// - i: A valid index of the collection.
227+
/// - distance: The distance to offset `i`.
228+
/// `distance` may be negative.
229+
///
230+
/// - Returns: An index offset by `distance` from the index `i`.
231+
/// If `distance` is positive, this is the same value as the result of `distance` calls to `index(after:)`.
232+
/// If `distance` is negative, this is the same value as the result of `abs(distance)` calls to `index(before:)`.
233+
/// If the index which would be `distance` from `i` would lie outside this collection, this returns `nil`.
234+
func indexOrNil(_ i: Index, offsetBy distance: Int) -> Index? {
235+
if distance > 0 {
236+
index(i, offsetBy: distance, limitedBy: endIndex)
237+
}
238+
else if distance < 0 {
239+
index(i, offsetBy: distance, limitedBy: startIndex)
240+
}
241+
else {
242+
i
243+
}
244+
}
245+
}

0 commit comments

Comments
 (0)