Skip to content

Commit 48bb120

Browse files
committed
✨ Nested NonEmpty now throws as expected
1 parent 51ef544 commit 48bb120

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
lines changed

Sources/NonEmpty/NonEmpty+Error.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ extension NonEmpty {
33
enum Error: Swift.Error, CustomStringConvertible {
44

55
case emptyCollection
6-
6+
case tooFewElements(expected: Int)
7+
78
var description: String {
89
switch self {
910
case .emptyCollection:
1011
return "Non-empty collection expected"
12+
case let .tooFewElements(expected):
13+
return "Expected at least \(expected) element\(expected > 1 ? "s" : "")"
1114
}
1215
}
1316

Sources/NonEmpty/NonEmpty+Nested.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,37 @@ public func AtLeast10<C: Swift.Collection>(_ c: C) throws -> _AtLeast10<C> {
6969
try NonEmpty(AtLeast9(c))
7070
}
7171

72+
public func AtLeast1<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast1<NonEmpty<C>> {
73+
try NonEmpty(nonEmpty)
74+
}
75+
public func AtLeast2<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast2<NonEmpty<C>> {
76+
try NonEmpty(AtLeast1(nonEmpty))
77+
}
78+
public func AtLeast3<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast3<NonEmpty<C>> {
79+
try NonEmpty(AtLeast2(nonEmpty))
80+
}
81+
public func AtLeast4<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast4<NonEmpty<C>> {
82+
try NonEmpty(AtLeast3(nonEmpty))
83+
}
84+
public func AtLeast5<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast5<NonEmpty<C>> {
85+
try NonEmpty(AtLeast4(nonEmpty))
86+
}
87+
public func AtLeast6<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast6<NonEmpty<C>> {
88+
try NonEmpty(AtLeast5(nonEmpty))
89+
}
90+
public func AtLeast7<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast7<NonEmpty<C>> {
91+
try NonEmpty(AtLeast6(nonEmpty))
92+
}
93+
public func AtLeast8<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast8<NonEmpty<C>> {
94+
try NonEmpty(AtLeast7(nonEmpty))
95+
}
96+
public func AtLeast9<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast9<NonEmpty<C>> {
97+
try NonEmpty(AtLeast8(nonEmpty))
98+
}
99+
public func AtLeast10<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws -> _AtLeast10<NonEmpty<C>> {
100+
try NonEmpty(AtLeast9(nonEmpty))
101+
}
102+
72103
fileprivate typealias _AtLeast11<C: Swift.Collection> = NonEmpty<_AtLeast10<C>>
73104
extension _AtLeast11 {
74105
public var drop10: NonEmpty<Collection.SubSequence> {

Sources/NonEmpty/NonEmpty.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ public struct NonEmpty<Collection: Swift.Collection>: Swift.Collection {
66
public var head: Collection.Element { self[self.startIndex] }
77
public internal(set) var tail: Slice<Collection>
88

9+
public let minimumCount: Int
10+
911
public init(from rawValue: Slice<Collection>) throws {
1012
guard !rawValue.isEmpty else {
1113
throw Self.Error.emptyCollection
1214
}
1315
let bounds = (rawValue.index(after: rawValue.startIndex))..<rawValue.endIndex
1416
self.tail = Slice(base: rawValue.base, bounds: bounds)
17+
self.minimumCount = 1
1518
}
1619

1720
public init(from rawValue: Collection) throws {
@@ -23,6 +26,10 @@ public struct NonEmpty<Collection: Swift.Collection>: Swift.Collection {
2326
}
2427

2528
public init<C: Swift.Collection>(_ nonEmpty: NonEmpty<C>) throws where Collection == NonEmpty<C> {
29+
self.minimumCount = nonEmpty.minimumCount + 1
30+
guard !nonEmpty.tail.dropFirst(nonEmpty.minimumCount).isEmpty else {
31+
throw Self.Error.tooFewElements(expected: self.minimumCount)
32+
}
2633
let tail = try NonEmpty<C>(from: nonEmpty.tail)
2734
self.tail = Slice(base: nonEmpty, bounds: tail.startIndex..<tail.endIndex)
2835
}

Tests/NonEmptyTests/NonEmptyTests.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,15 +240,38 @@ final class NonEmptyTests: XCTestCase {
240240
XCTAssertEqual(atLeast10.eighth, 8)
241241
XCTAssertEqual(atLeast10.ninth, 9)
242242
XCTAssertEqual(atLeast10.tenth, 10)
243-
244-
let exactly21 = try AtLeast10(AtLeast10([0] + digits + digits.map { $0 + 10 }))
243+
244+
let nonEmpty = try NonEmpty(from: digits)
245+
XCTAssertEqual(nonEmpty.minimumCount, 1)
246+
247+
let atLeast2 = try AtLeast2(digits)
248+
XCTAssertEqual(atLeast2.minimumCount, 2)
249+
250+
let atLeast4 = try AtLeast2(AtLeast2(digits))
251+
XCTAssertEqual(atLeast4.minimumCount, 4)
252+
253+
let exactly21 = try AtLeast10(AtLeast10(Array(0...20)))
245254
XCTAssertEqual(exactly21.count, 21)
246255
XCTAssertEqual(exactly21[12], 12)
247256

248257
let atLeast42 = try AtLeast10(AtLeast10(AtLeast10(AtLeast10(AtLeast2(Array(1...100))))))
249258
XCTAssertEqual(atLeast42.count, 100)
250259
XCTAssertEqual(atLeast42.drop10.drop10.count, 80)
251260
XCTAssertEqual(atLeast42.drop10.drop10.drop10.drop10.second, 42)
261+
262+
XCTAssertThrowsError(try NonEmpty(NonEmpty(from: digits.prefix(1))))
263+
XCTAssertThrowsError(try AtLeast2(digits.prefix(1)))
264+
XCTAssertThrowsError(try AtLeast3(digits.prefix(2)))
265+
XCTAssertThrowsError(try AtLeast4(digits.prefix(3)))
266+
XCTAssertThrowsError(try AtLeast5(digits.prefix(4)))
267+
XCTAssertThrowsError(try AtLeast6(digits.prefix(5)))
268+
XCTAssertThrowsError(try AtLeast7(digits.prefix(6)))
269+
XCTAssertThrowsError(try AtLeast8(digits.prefix(7)))
270+
XCTAssertThrowsError(try AtLeast9(digits.prefix(8)))
271+
XCTAssertThrowsError(try AtLeast10(digits.prefix(9)))
272+
XCTAssertThrowsError(try AtLeast10(AtLeast10(Array(1...19))), (try! AtLeast10(AtLeast10(Array(1...19)))).minimumCount.description)
273+
274+
XCTAssertNoThrow(try AtLeast10(AtLeast10(Array(1...20))))
252275
}
253276
}
254277

0 commit comments

Comments
 (0)