diff --git a/Sources/NonEmpty/NonEmpty+Error.swift b/Sources/NonEmpty/NonEmpty+Error.swift new file mode 100644 index 0000000..e7a98d1 --- /dev/null +++ b/Sources/NonEmpty/NonEmpty+Error.swift @@ -0,0 +1,15 @@ +enum NonEmptyError: Swift.Error, CustomStringConvertible { + + case emptyCollection + case tooFewElements(expected: Int) + + var description: String { + switch self { + case .emptyCollection: + return "Non-empty collection expected" + case let .tooFewElements(expected): + return "Expected at least \(expected) element\(expected > 1 ? "s" : "")" + } + } + +} diff --git a/Sources/NonEmpty/NonEmpty+Nested.swift b/Sources/NonEmpty/NonEmpty+Nested.swift new file mode 100644 index 0000000..7f0aa1f --- /dev/null +++ b/Sources/NonEmpty/NonEmpty+Nested.swift @@ -0,0 +1,341 @@ +public typealias AtLeast1 = NonEmpty +public typealias AtLeast2 = NonEmpty> +public typealias AtLeast3 = NonEmpty> +public typealias AtLeast4 = NonEmpty> +public typealias AtLeast5 = NonEmpty> +public typealias AtLeast6 = NonEmpty> +public typealias AtLeast7 = NonEmpty> +public typealias AtLeast8 = NonEmpty> +public typealias AtLeast9 = NonEmpty> +public typealias AtLeast10 = NonEmpty> + +extension NonEmptyProtocol { + @_disfavoredOverload + public init(_ c: Collection) throws { + try self.init(from: c) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol +{ + public var second: Element { self[self.index(self.startIndex, offsetBy: 1)] } + public init(_ c: Collection.Collection) throws { + try self.init(.init(c)) + } + public init( + _ e1: Element, + _ e2: Element, + tail: Collection.Collection + ) where Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ tail: Element... + ) where Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol +{ + public var third: Element { self[self.index(self.startIndex, offsetBy: 2)] } + public init(_ c: Collection.Collection.Collection) throws { + try self.init(.init(.init(c))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + tail: Collection.Collection.Collection + ) where Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ tail: Element... + ) where Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection: NonEmptyProtocol +{ + public var fourth: Element { self[self.index(self.startIndex, offsetBy: 3)] } + public init(_ c: Collection.Collection.Collection.Collection) throws { + try self.init(.init(.init(.init(c)))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + tail: Collection.Collection.Collection.Collection + ) where Collection.Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3, e4], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ tail: Element... + ) where Collection.Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, e4, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection: NonEmptyProtocol +{ + public var fifth: Element { self[self.index(self.startIndex, offsetBy: 4)] } + public init(_ c: Collection.Collection.Collection.Collection.Collection) throws { + try self.init(.init(.init(.init(.init(c))))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + tail: Collection.Collection.Collection.Collection.Collection + ) where Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3, e4, e5], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ tail: Element... + ) where Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, e4, e5, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol +{ + public var sixth: Element { self[self.index(self.startIndex, offsetBy: 5)] } + public init(_ c: Collection.Collection.Collection.Collection.Collection.Collection) throws { + try self.init(.init(.init(.init(.init(.init(c)))))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + tail: Collection.Collection.Collection.Collection.Collection.Collection + ) where Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3, e4, e5, e6], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ tail: Element... + ) where Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, e4, e5, e6, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol +{ + public var seventh: Element { self[self.index(self.startIndex, offsetBy: 6)] } + public init(_ c: Collection.Collection.Collection.Collection.Collection.Collection.Collection) throws { + try self.init(.init(.init(.init(.init(.init(.init(c))))))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + tail: Collection.Collection.Collection.Collection.Collection.Collection.Collection + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3, e4, e5, e6, e7], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + _ tail: Element... + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, e4, e5, e6, e7, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol +{ + public var eighth: Element { self[self.index(self.startIndex, offsetBy: 7)] } + public init(_ c: Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection) throws { + try self.init(.init(.init(.init(.init(.init(.init(.init(c)))))))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + _ e8: Element, + tail: Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3, e4, e5, e6, e7, e8], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + _ e8: Element, + _ tail: Element... + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, e4, e5, e6, e7, e8, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol +{ + public var ninth: Element { self[self.index(self.startIndex, offsetBy: 8)] } + public init(_ c: Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection) throws { + try self.init(.init(.init(.init(.init(.init(.init(.init(.init(c))))))))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + _ e8: Element, + _ e9: Element, + tail: Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3, e4, e5, e6, e7, e8, e9], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + _ e8: Element, + _ e9: Element, + _ tail: Element... + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, e4, e5, e6, e7, e8, e9, tail: .init(tail)) + } +} +extension NonEmptyProtocol +where Collection: NonEmptyProtocol, + Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol, + Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: NonEmptyProtocol +{ + public var tenth: Element { self[self.index(self.startIndex, offsetBy: 9)] } + public init(_ c: Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection) throws { + try self.init(.init(.init(.init(.init(.init(.init(.init(.init(.init(c)))))))))) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + _ e8: Element, + _ e9: Element, + _ e10: Element, + tail: Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + var rawValue = tail + rawValue.insert(contentsOf: [e1, e2, e3, e4, e5, e6, e7, e8, e9, e10], at: rawValue.startIndex) + try! self.init(rawValue) + } + public init( + _ e1: Element, + _ e2: Element, + _ e3: Element, + _ e4: Element, + _ e5: Element, + _ e6: Element, + _ e7: Element, + _ e8: Element, + _ e9: Element, + _ e10: Element, + _ tail: Element... + ) where Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection.Collection: RangeReplaceableCollection { + self.init(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, tail: .init(tail)) + } +} diff --git a/Sources/NonEmpty/NonEmpty+RangeReplaceableCollection.swift b/Sources/NonEmpty/NonEmpty+RangeReplaceableCollection.swift index 2e339a4..0affd6a 100644 --- a/Sources/NonEmpty/NonEmpty+RangeReplaceableCollection.swift +++ b/Sources/NonEmpty/NonEmpty+RangeReplaceableCollection.swift @@ -1,15 +1,25 @@ // NB: `NonEmpty` does not conditionally conform to `RangeReplaceableCollection` because it contains destructive methods. -extension NonEmpty where Collection: RangeReplaceableCollection { - public init(_ head: Element, _ tail: Element...) { +extension NonEmptyProtocol where Collection: RangeReplaceableCollection { + public init(_ head: Element, tail: Collection) { var tail = tail tail.insert(head, at: tail.startIndex) - self.init(rawValue: Collection(tail))! + try! self.init(from: tail) + } + + public init(_ head: Element, _ tail: Element...) { + self.init(head, tail: Collection(tail)) + } + + public init(from elements: S) throws where S: Sequence, Collection.Element == S.Element { + try self.init(from: Collection(elements)) } public init?(_ elements: S) where S: Sequence, Collection.Element == S.Element { - self.init(rawValue: Collection(elements)) + try? self.init(from: elements) } +} +extension NonEmpty where RawValue: RangeReplaceableCollection { public mutating func append(_ newElement: Element) { self.rawValue.append(newElement) } @@ -51,6 +61,69 @@ extension NonEmpty where Collection: RangeReplaceableCollection { } } +extension NonEmpty { + public mutating func append(_ newElement: Element) + where Self == NonEmpty> + { + self.rawValue.append(newElement) + } + + public mutating func append(contentsOf newElements: S) + where Self == NonEmpty>, + S.Element == C.Element + { + self.rawValue.append(contentsOf: newElements) + } + + public mutating func insert(_ newElement: Element, at i: Index) + where Self == NonEmpty> + { + self.rawValue.insert(newElement, at: i) + } + + public mutating func insert( + contentsOf newElements: S, at i: Index + ) + where Self == NonEmpty>, + S: Swift.Collection, + Element == S.Element + { + self.rawValue.insert(contentsOf: newElements, at: i) + } + + public static func += (lhs: inout Self, rhs: S) + where Self == NonEmpty>, + S.Element == C.Element + { + lhs.append(contentsOf: rhs) + } + + public static func + (lhs: Self, rhs: Self) -> Self + where Self == NonEmpty> + { + var lhs = lhs + lhs += rhs + return lhs + } + + public static func + (lhs: Self, rhs: S) -> Self + where Self == NonEmpty>, + S.Element == C.Element + { + var lhs = lhs + lhs += rhs + return lhs + } + +// public static func + (lhs: S, rhs: Self) -> Self +// where Self == NonEmpty>, +// S.Element == C.Element +// { +// rhs.insert(contentsOf: ContiguousArray(lhs), at: rhs.startIndex) +// return rhs +// } +} + extension NonEmpty { public func joined( separator: S diff --git a/Sources/NonEmpty/NonEmpty.swift b/Sources/NonEmpty/NonEmpty.swift index e5dcc23..f875037 100644 --- a/Sources/NonEmpty/NonEmpty.swift +++ b/Sources/NonEmpty/NonEmpty.swift @@ -1,15 +1,33 @@ @dynamicMemberLookup -public struct NonEmpty: Swift.Collection { +public struct NonEmpty: Swift.Collection, NonEmptyProtocol { public typealias Element = Collection.Element public typealias Index = Collection.Index public internal(set) var rawValue: Collection - public init?(rawValue: Collection) { - guard !rawValue.isEmpty else { return nil } + public static var minimumCount: Int { + if let T = Collection.self as? WithMinimumCount.Type { + return T.minimumCount + 1 + } else { + return 1 + } + } + + public init(from rawValue: Collection) throws { + guard !rawValue.dropFirst(Self.minimumCount - 1).isEmpty else { + if Self.minimumCount == 1 { + throw NonEmptyError.emptyCollection + } else { + throw NonEmptyError.tooFewElements(expected: Self.minimumCount) + } + } self.rawValue = rawValue } + public init?(rawValue: Collection) { + try? self.init(from: rawValue) + } + public subscript(dynamicMember keyPath: KeyPath) -> Subject { self.rawValue[keyPath: keyPath] } @@ -24,8 +42,6 @@ public struct NonEmpty: Swift.Collection { self.rawValue.index(after: i) } - public var first: Element { self.rawValue.first! } - public func max(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element { try self.rawValue.max(by: areInIncreasingOrder)! } @@ -119,6 +135,9 @@ extension NonEmpty where Collection.Element: Comparable { } extension NonEmpty: BidirectionalCollection where Collection: BidirectionalCollection { + public var last: Element { self.rawValue.last! } +} +extension NonEmptyProtocol where Collection: BidirectionalCollection { public func index(before i: Index) -> Index { self.rawValue.index(before: i) } diff --git a/Sources/NonEmpty/NonEmptyProtocol.swift b/Sources/NonEmpty/NonEmptyProtocol.swift new file mode 100644 index 0000000..81a3706 --- /dev/null +++ b/Sources/NonEmpty/NonEmptyProtocol.swift @@ -0,0 +1,22 @@ +/// - Note: We need to separate `WithMinimumCount` from `NonEmptyProtocol` +/// to avoid the "Self or associated type requirement". +public protocol WithMinimumCount { + static var minimumCount: Int { get } +} + +public protocol NonEmptyProtocol: Swift.Collection, RawRepresentable, WithMinimumCount +where Element == RawValue.Element, + Index == RawValue.Index, + Collection == RawValue +{ + associatedtype Collection: Swift.Collection + init(from rawValue: Collection) throws +} + +extension NonEmptyProtocol { + public var first: Element { self[self.startIndex] } +} + +internal protocol _NonEmptyProtocol: NonEmptyProtocol { + var rawValue: RawValue { get set } +} diff --git a/Tests/NonEmptyTests/NonEmptyTests.swift b/Tests/NonEmptyTests/NonEmptyTests.swift index 05e7d02..2efa5f5 100644 --- a/Tests/NonEmptyTests/NonEmptyTests.swift +++ b/Tests/NonEmptyTests/NonEmptyTests.swift @@ -26,6 +26,8 @@ final class NonEmptyTests: XCTestCase { XCTAssertEqual([1, 2, 3], Array(xs)) XCTAssertEqual(NonEmptyArray(1, 2, 3, 1, 2, 3), xs + xs) + + XCTAssertThrowsError(try NonEmptyArray(from: [])) } func testBidirectionalCollection() { @@ -215,6 +217,118 @@ final class NonEmptyTests: XCTestCase { XCTAssertEqual(.init("A", "C", "B"), xs) } #endif + + func testNestedNonEmpty() throws { + // Test type safe accessors + let digits = Array(1...10) + try XCTAssertEqual(AtLeast1(digits).first, 1) + try XCTAssertEqual(AtLeast2(digits).second, 2) + try XCTAssertEqual(AtLeast3(digits).third, 3) + try XCTAssertEqual(AtLeast4(digits).fourth, 4) + try XCTAssertEqual(AtLeast5(digits).fifth, 5) + try XCTAssertEqual(AtLeast6(digits).sixth, 6) + try XCTAssertEqual(AtLeast7(digits).seventh, 7) + try XCTAssertEqual(AtLeast8(digits).eighth, 8) + try XCTAssertEqual(AtLeast9(digits).ninth, 9) + try XCTAssertEqual(AtLeast10(digits).tenth, 10) + + // Test type safe accessors on more nested types + let atLeast10Digits = try AtLeast10(digits) + XCTAssertEqual(atLeast10Digits.first, 1) + XCTAssertEqual(atLeast10Digits.second, 2) + XCTAssertEqual(atLeast10Digits.third, 3) + XCTAssertEqual(atLeast10Digits.fourth, 4) + XCTAssertEqual(atLeast10Digits.fifth, 5) + XCTAssertEqual(atLeast10Digits.sixth, 6) + XCTAssertEqual(atLeast10Digits.seventh, 7) + XCTAssertEqual(atLeast10Digits.eighth, 8) + XCTAssertEqual(atLeast10Digits.ninth, 9) + XCTAssertEqual(atLeast10Digits.tenth, 10) + + // Test `minimumCount` + XCTAssertEqual( NonEmpty<[Int]> .minimumCount, 1) + XCTAssertEqual( AtLeast2<[Int]> .minimumCount, 2) + XCTAssertEqual(AtLeast2>.minimumCount, 4) + + // Test count and access by index + let exactly21Numbers = try AtLeast8(AtLeast8(Array(0...20))) + XCTAssertEqual(exactly21Numbers.count, 21) + XCTAssertEqual(exactly21Numbers[12], 12) + + // Test initializers correctly throw + XCTAssertThrowsError(try AtLeast2(digits.prefix(1))) + XCTAssertThrowsError(try AtLeast3(digits.prefix(2))) + XCTAssertThrowsError(try AtLeast4(digits.prefix(3))) + XCTAssertThrowsError(try AtLeast5(digits.prefix(4))) + XCTAssertThrowsError(try AtLeast6(digits.prefix(5))) + XCTAssertThrowsError(try AtLeast7(digits.prefix(6))) + XCTAssertThrowsError(try AtLeast8(digits.prefix(7))) + XCTAssertThrowsError(try AtLeast9(digits.prefix(8))) + XCTAssertThrowsError(try AtLeast10(digits.prefix(9))) + XCTAssertThrowsError(try AtLeast10(AtLeast10(Array(1...19)))) + XCTAssertThrowsError(try NonEmpty(NonEmpty([1]))) + XCTAssertThrowsError(try AtLeast2(AtLeast2([1, 2, 3]))) + + // Test initializers correctly **not** throw + XCTAssertNoThrow(try AtLeast8(AtLeast8(Array(1...16)))) + + // Test nested `NonEmpty` can be initialized in a safe way + XCTAssertEqual(NonEmpty (1, tail: [2, 3]).first, 1) + XCTAssertEqual(NonEmpty<[Int]> (1, 2, 3) .first, 1) + XCTAssertEqual(NonEmpty<[Int]> (1) .first, 1) + XCTAssertEqual(AtLeast1 (1, tail: [2, 3]).first, 1) + XCTAssertEqual(AtLeast1<[Int]> (1, 2, 3) .first, 1) + XCTAssertEqual(AtLeast1<[Int]> (1) .first, 1) + XCTAssertEqual(AtLeast2 (1, 2, tail: [3, 4]).second, 2) + XCTAssertEqual(AtLeast2<[Int]> (1, 2, 3, 4) .second, 2) + XCTAssertEqual(AtLeast2<[Int]> (1, 2) .second, 2) + XCTAssertEqual(AtLeast3 (1, 2, 3, tail: [4, 5]).third, 3) + XCTAssertEqual(AtLeast3<[Int]> (1, 2, 3, 4, 5) .third, 3) + XCTAssertEqual(AtLeast3<[Int]> (1, 2, 3) .third, 3) + XCTAssertEqual(AtLeast4 (1, 2, 3, 4, tail: [5, 6]).fourth, 4) + XCTAssertEqual(AtLeast4<[Int]> (1, 2, 3, 4, 5, 6) .fourth, 4) + XCTAssertEqual(AtLeast4<[Int]> (1, 2, 3, 4) .fourth, 4) + XCTAssertEqual(AtLeast5 (1, 2, 3, 4, 5, tail: [6, 7]).fifth, 5) + XCTAssertEqual(AtLeast5<[Int]> (1, 2, 3, 4, 5, 6, 7) .fifth, 5) + XCTAssertEqual(AtLeast5<[Int]> (1, 2, 3, 4, 5) .fifth, 5) + XCTAssertEqual(AtLeast6 (1, 2, 3, 4, 5, 6, tail: [7, 8]).sixth, 6) + XCTAssertEqual(AtLeast6<[Int]> (1, 2, 3, 4, 5, 6, 7, 8) .sixth, 6) + XCTAssertEqual(AtLeast6<[Int]> (1, 2, 3, 4, 5, 6) .sixth, 6) + XCTAssertEqual(AtLeast7 (1, 2, 3, 4, 5, 6, 7, tail: [8, 9]).seventh, 7) + XCTAssertEqual(AtLeast7<[Int]> (1, 2, 3, 4, 5, 6, 7, 8, 9) .seventh, 7) + XCTAssertEqual(AtLeast7<[Int]> (1, 2, 3, 4, 5, 6, 7) .seventh, 7) + XCTAssertEqual(AtLeast8 (1, 2, 3, 4, 5, 6, 7, 8, tail: [9, 10]).eighth, 8) + XCTAssertEqual(AtLeast8<[Int]> (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) .eighth, 8) + XCTAssertEqual(AtLeast8<[Int]> (1, 2, 3, 4, 5, 6, 7, 8) .eighth, 8) + XCTAssertEqual(AtLeast9 (1, 2, 3, 4, 5, 6, 7, 8, 9, tail: [10, 11]).ninth, 9) + XCTAssertEqual(AtLeast9<[Int]> (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) .ninth, 9) + XCTAssertEqual(AtLeast9<[Int]> (1, 2, 3, 4, 5, 6, 7, 8, 9) .ninth, 9) + XCTAssertEqual(AtLeast10 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, tail: [11, 12]).tenth, 10) + XCTAssertEqual(AtLeast10<[Int]>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) .tenth, 10) + XCTAssertEqual(AtLeast10<[Int]>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) .tenth, 10) + + // Test that appending to a nested `NonEmpty` works as expected + var mutableDigits = AtLeast2<[Int]>(1, 2) + XCTAssertEqual(Array(mutableDigits), [1, 2]) + XCTAssertEqual(mutableDigits.first, 1) + XCTAssertEqual(mutableDigits.last, 2) + mutableDigits.append(3) + XCTAssertEqual(Array(mutableDigits), [1, 2, 3]) + XCTAssertEqual(mutableDigits.first, 1) + XCTAssertEqual(mutableDigits.last, 3) + + // Test some code does not compile + // Note: I couldn't find a way to assert this, so one way to check it is to uncomment the code +// _ = try AtLeast1(digits).second +// _ = try AtLeast2(digits).third +// _ = try AtLeast3(digits).fourth +// _ = try AtLeast4(digits).fifth +// _ = try AtLeast5(digits).sixth +// _ = try AtLeast6(digits).seventh +// _ = try AtLeast7(digits).eighth +// _ = try AtLeast8(digits).ninth +// _ = try AtLeast9(digits).tenth + } } struct TrivialHashable: Equatable, Comparable, Hashable {