// // NonCopyableLinkedList.swift // damus // // Created by Daniel D’Aquino on 2025-07-04. // /// A linked list to help with iteration of non-copyable elements /// /// This is needed to provide an array-like abstraction or iterators since swift arrays or iterator protocols require the element to be "copyable" struct NonCopyableLinkedList: ~Copyable { private var head: Node? = nil private var tail: Node? = nil private(set) var count: Int = 0 /// Iterates over each item of the list, with enumeration support. func forEachItem(_ borrowingFunction: ((_ index: Int, _ item: borrowing T) throws -> LoopCommand)) rethrows -> Y? { var indexCounter = 0 var cursor: Node? = self.head outerLoop: while let nextItem = cursor { let loopIterationResult = try borrowingFunction(indexCounter, nextItem.value) indexCounter += 1 cursor = nextItem.next switch loopIterationResult { case .loopBreak: break outerLoop case .loopContinue: continue outerLoop case .loopReturn(let result): return result } } return nil } /// Iterates over each item of the list in reverse, with enumeration support. func forEachItemReversed(_ borrowingFunction: ((_ index: Int, _ item: borrowing T) throws(E) -> LoopCommand)) throws(E) -> Y? { var indexCounter = count var cursor: Node? = self.tail outerLoop: while let nextItem = cursor { let loopIterationResult = try borrowingFunction(indexCounter, nextItem.value) indexCounter -= 1 cursor = nextItem.previous switch loopIterationResult { case .loopBreak: break outerLoop case .loopContinue: continue outerLoop case .loopReturn(let result): return result } } return nil } /// Iterates over each item of the list, with enumeration support, updating some value in each iteration and returning the final value at the end. func reduce(initialResult: Y, _ borrowingFunction: ((_ index: Int, _ partialResult: Y, _ item: borrowing T) throws -> LoopCommand)) throws -> Y { var indexCounter = 0 var currentResult = initialResult var cursor: Node? = self.head outerLoop: while let nextItem = cursor { let loopIterationResult = try borrowingFunction(indexCounter, currentResult, nextItem.value) indexCounter += 1 cursor = nextItem.next switch loopIterationResult { case .loopBreak: break outerLoop case .loopContinue: continue outerLoop case .loopReturn(let result): currentResult = result continue outerLoop } } return currentResult } /// Uses a specific item of the list based on a provided index. /// /// O(N/2) worst case scenario /// /// Returns `nil` if nothing was found func useItem(at index: Int, _ borrowingFunction: ((_ item: borrowing T) throws -> Y)) rethrows -> Y? { if index < 0 || index >= self.count { return nil } else if index < self.count / 2 { return try self.forEachItem({ i, item in if i == index { return .loopReturn(try borrowingFunction(item)) } return .loopContinue }) } else { return try self.forEachItemReversed({ i, item in if i == index { return .loopReturn(try borrowingFunction(item)) } return .loopContinue }) } } /// Adds an item to the tail end list mutating func add(item: consuming T) { guard self.head != nil, let currentTail = self.tail else { let firstNode = Node(value: item, next: nil, previous: nil) self.head = firstNode self.tail = firstNode self.count = 1 return } let newTail = Node(value: item, next: nil, previous: currentTail) currentTail.next = newTail self.tail = newTail self.count += 1 } /// A node of the linked list /// /// Should be `~Copyable` but that would require using a value type such as a struct or enum, and the Swift compiler does not support recursive enums with non-copyable objects for some reason. Example: /// ```swift /// enum List: ~Copyable { /// indirect case node(value: Y, next: NewList) // <-- ERROR: Noncopyable enum 'List' cannot be marked indirect or have indirect cases yet /// case empty /// } /// ``` /// /// Therefore, we make it `private` to make sure we contain the exposure of this unsafe object to only this class. Outside users of the linked list can access objects via the iterator functions. private class Node { let value: Item var next: Node? var previous: Node? init(value: consuming Item, next: consuming Node?, previous: consuming Node?) { self.value = value self.next = next self.previous = previous } } /// A loop command to allow closures to control the loop they are in. enum LoopCommand { /// Breaks out of the loop case loopBreak /// Continues to the next iteration of the loop case loopContinue /// Stops iterating and return a value case loopReturn(Y) } }