From 9bc5a57c3b9d3b4707d016f91397dae114b7bb9e Mon Sep 17 00:00:00 2001 From: Tristan Labelle Date: Mon, 8 Apr 2024 11:58:12 -0400 Subject: [PATCH] Added methods to box Swift collections into WinRT-compatible collections. (#130) --- .../ProjectionModel/SupportModules.swift | 4 +- .../Array+toWinRTIVector.swift | 81 +++++++++++++++++++ .../Collection+toWinRTIVectorView.swift | 51 ++++++++++++ .../IIterable.swift | 2 + .../IIterator.swift | 2 + .../IVector.swift | 70 ++++++++++++++++ .../IVectorView.swift | 38 +++++++++ .../Sequence+toWinRTIIterable.swift | 50 ++++++++++++ 8 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Array+toWinRTIVector.swift create mode 100644 Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Collection+toWinRTIVectorView.swift rename Support/Sources/WindowsRuntime/ProjectedTypes/{WindowsFoundation => WindowsFoundationCollections}/IIterable.swift (93%) rename Support/Sources/WindowsRuntime/ProjectedTypes/{WindowsFoundation => WindowsFoundationCollections}/IIterator.swift (94%) create mode 100644 Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVector.swift create mode 100644 Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVectorView.swift create mode 100644 Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Sequence+toWinRTIIterable.swift diff --git a/Generator/Sources/ProjectionModel/SupportModules.swift b/Generator/Sources/ProjectionModel/SupportModules.swift index 3168e334..8bca095a 100644 --- a/Generator/Sources/ProjectionModel/SupportModules.swift +++ b/Generator/Sources/ProjectionModel/SupportModules.swift @@ -128,7 +128,9 @@ extension SupportModules.WinRT { ] internal static let windowsFoundationCollections = [ "IIterable`1", - "IIterator`1" + "IIterator`1", + "IVector`1", + "IVectorView`1" ] } diff --git a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Array+toWinRTIVector.swift b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Array+toWinRTIVector.swift new file mode 100644 index 00000000..6eb4ac9f --- /dev/null +++ b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Array+toWinRTIVector.swift @@ -0,0 +1,81 @@ +extension Array { + public func toWinRTIVector(elementEquals: @escaping (Element, Element) -> Bool) -> ArrayVector { + ArrayVector(self, elementEquals: elementEquals) + } +} + +extension Array where Element: Equatable { + public func toWinRTIVector() -> ArrayVector { + ArrayVector(self, elementEquals: ==) + } +} + +/// Wraps a Swift array into a type implementing WinRT's Windows.Foundation.Collections.IVector. +public class ArrayVector: WinRTExport, + WindowsFoundationCollections_IVectorProtocol, WindowsFoundationCollections_IVectorViewProtocol { + public var array: [T] + public var elementEquals: (T, T) -> Bool + + init(_ array: [T], elementEquals: @escaping (T, T) -> Bool) { + self.array = array + self.elementEquals = elementEquals + } + + public func _size() throws -> UInt32 { + UInt32(array.count) + } + + public func first() throws -> WindowsFoundationCollections_IIterator { + SequenceIterator(array.makeIterator()) + } + + public func getAt(_ index: UInt32) throws -> T { + array[Int(index)] + } + + public func getView() throws -> WindowsFoundationCollections_IVectorView { + array.toWinRTIVectorView(elementEquals: elementEquals) + } + + public func indexOf(_ value: T, _ index: inout UInt32) throws -> Bool { + if let foundIndex = array.firstIndex(where: { elementEquals($0, value) }) { + index = UInt32(foundIndex) + return true + } else { + index = 0 + return false + } + } + + public func setAt(_ index: UInt32, _ value: T) throws { + array[Int(index)] = value + } + + public func insertAt(_ index: UInt32, _ value: T) throws { + array.insert(value, at: Int(index)) + } + + public func removeAt(_ index: UInt32) throws { + array.remove(at: Int(index)) + } + + public func append(_ value: T) throws { + array.append(value) + } + + public func removeAtEnd() throws { + array.removeLast() + } + + public func clear() throws { + array.removeAll() + } + + public func getMany(_ startIndex: UInt32, _ items: [T]) throws -> UInt32 { + throw HResult.Error.notImpl // TODO(#31): Implement out arrays + } + + public func replaceAll(_ items: [T]) throws { + array = items + } +} diff --git a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Collection+toWinRTIVectorView.swift b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Collection+toWinRTIVectorView.swift new file mode 100644 index 00000000..84a872a1 --- /dev/null +++ b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Collection+toWinRTIVectorView.swift @@ -0,0 +1,51 @@ +extension Collection where Index == Int { + public func toWinRTIVectorView(elementEquals: @escaping (Element, Element) -> Bool) -> WindowsFoundationCollections_IVectorView { + CollectionVectorView(self, elementEquals: elementEquals) + } +} + +extension Collection where Index == Int, Element: Equatable { + public func toWinRTIVectorView() -> WindowsFoundationCollections_IVectorView { + CollectionVectorView(self, elementEquals: ==) + } +} + +fileprivate class CollectionVectorView: WinRTExport, + WindowsFoundationCollections_IVectorViewProtocol + where C.Index == Int { + public typealias T = C.Element + + public var collection: C + public var elementEquals: (T, T) -> Bool + + init(_ collection: C, elementEquals: @escaping (T, T) -> Bool) { + self.collection = collection + self.elementEquals = elementEquals + } + + public func _size() throws -> UInt32 { + UInt32(collection.count) + } + + public func first() throws -> WindowsFoundationCollections_IIterator { + SequenceIterator(collection.makeIterator()) + } + + public func getAt(_ index: UInt32) throws -> C.Element { + collection[Int(index)] + } + + public func indexOf(_ value: C.Element, _ index: inout UInt32) throws -> Bool { + if let foundIndex = collection.firstIndex(where: { elementEquals($0, value) }) { + index = UInt32(foundIndex) + return true + } else { + index = 0 + return false + } + } + + public func getMany(_ startIndex: UInt32, _ items: [C.Element]) throws -> UInt32 { + throw HResult.Error.notImpl // TODO(#31): Implement out arrays + } +} \ No newline at end of file diff --git a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundation/IIterable.swift b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IIterable.swift similarity index 93% rename from Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundation/IIterable.swift rename to Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IIterable.swift index d2dbee01..ddec2543 100644 --- a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundation/IIterable.swift +++ b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IIterable.swift @@ -1,3 +1,5 @@ +// Public types and protocols in here must be compatible with what the code generator would emit. + /// Exposes an iterator that supports simple iteration over a collection of a specified type. public typealias WindowsFoundationCollections_IIterable = any WindowsFoundationCollections_IIterableProtocol diff --git a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundation/IIterator.swift b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IIterator.swift similarity index 94% rename from Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundation/IIterator.swift rename to Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IIterator.swift index 347e8533..4348de6e 100644 --- a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundation/IIterator.swift +++ b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IIterator.swift @@ -1,3 +1,5 @@ +// Public types and protocols in here must be compatible with what the code generator would emit. + /// Supports simple iteration over a collection. public typealias WindowsFoundationCollections_IIterator = any WindowsFoundationCollections_IIteratorProtocol diff --git a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVector.swift b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVector.swift new file mode 100644 index 00000000..978bd50c --- /dev/null +++ b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVector.swift @@ -0,0 +1,70 @@ +// Public types and protocols in here must be compatible with what the code generator would emit. + +/// Represents a random-access collection of elements. +public typealias WindowsFoundationCollections_IVector = any WindowsFoundationCollections_IVectorProtocol + +/// Represents a random-access collection of elements. +public protocol WindowsFoundationCollections_IVectorProtocol: WindowsFoundationCollections_IIterableProtocol { + associatedtype T + + /// Returns the item at the specified index in the vector. + /// - Parameter index: The zero-based index of the item. + /// - Returns: The item at the specified index. + func getAt(_ index: Swift.UInt32) throws -> T + + /// Returns an immutable view of the vector. + /// - Returns: The view of the vector. + func getView() throws -> WindowsFoundationCollections_IVectorView + + /// Retrieves the index of a specified item in the vector. + /// - Parameter value: The item to find in the vector. + /// - Parameter index: If the item is found, this is the zero-based index of the item; otherwise, this parameter is 0. + /// - Returns: **true** if the item is found; otherwise, **false**. + func indexOf(_ value: T, _ index: inout Swift.UInt32) throws -> Swift.Bool + + /// Sets the value at the specified index in the vector. + /// - Parameter index: The zero-based index at which to set the value. + /// - Parameter value: The item to set. + func setAt(_ index: Swift.UInt32, _ value: T) throws + + /// Inserts an item at a specified index in the vector. + /// - Parameter index: The zero-based index. + /// - Parameter value: The item to insert. + func insertAt(_ index: Swift.UInt32, _ value: T) throws + + /// Removes the item at the specified index in the vector. + /// - Parameter index: The zero-based index of the vector item to remove. + func removeAt(_ index: Swift.UInt32) throws + + /// Appends an item to the end of the vector. + /// - Parameter value: The item to append to the vector. + func append(_ value: T) throws + + /// Removes the last item from the vector. + func removeAtEnd() throws + + /// Removes all items from the vector. + func clear() throws + + /// Gets a collection of items from the vector beginning at the given index. + /// - Parameter startIndex: The zero-based index to start at. + /// - Parameter items: An array to copy the items into. + /// - Returns: A status code indicating the result of the operation. + func getMany(_ startIndex: Swift.UInt32, _ items: [T]) throws -> Swift.UInt32 + + /// Replaces all the items in the vector with the specified items. + /// - Parameter items: The collection of items to add to the vector. + func replaceAll(_ items: [T]) throws + + /// Gets the number of items in the vector. + /// - Returns: The number of items in the vector. + func _size() throws -> Swift.UInt32 +} + +extension WindowsFoundationCollections_IVectorProtocol { + /// Gets the number of items in the vector. + /// - Returns: The number of items in the vector. + public var size: Swift.UInt32 { + try! self._size() + } +} \ No newline at end of file diff --git a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVectorView.swift b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVectorView.swift new file mode 100644 index 00000000..c61a3924 --- /dev/null +++ b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/IVectorView.swift @@ -0,0 +1,38 @@ +// Public types and protocols in here must be compatible with what the code generator would emit. + +/// Represents an immutable view into a vector. +public typealias WindowsFoundationCollections_IVectorView = any WindowsFoundationCollections_IVectorViewProtocol + +/// Represents an immutable view into a vector. +public protocol WindowsFoundationCollections_IVectorViewProtocol: WindowsFoundationCollections_IIterableProtocol { + associatedtype T + + /// Returns the item at the specified index in the vector view. + /// - Parameter index: The zero-based index of the item. + /// - Returns: The item at the specified index. + func getAt(_ index: Swift.UInt32) throws -> T + + /// Retrieves the index of a specified item in the vector view. + /// - Parameter value: The item to find in the vector view. + /// - Parameter index: If the item is found, this is the zero-based index of the item; otherwise, this parameter is 0. + /// - Returns: **true** if the item is found; otherwise, **false**. + func indexOf(_ value: T, _ index: inout Swift.UInt32) throws -> Swift.Bool + + /// Gets a collection of items from the vector view beginning at the given index. + /// - Parameter startIndex: The zero-based index to start at. + /// - Parameter items: An array to copy the items into. + /// - Returns: A status code indicating the result of the operation. + func getMany(_ startIndex: Swift.UInt32, _ items: [T]) throws -> Swift.UInt32 + + /// Gets the number of items in the vector view. + /// - Returns: The number of items in the vector view. + func _size() throws -> Swift.UInt32 +} + +extension WindowsFoundationCollections_IVectorViewProtocol { + /// Gets the number of items in the vector view. + /// - Returns: The number of items in the vector view. + public var size: Swift.UInt32 { + try! self._size() + } +} \ No newline at end of file diff --git a/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Sequence+toWinRTIIterable.swift b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Sequence+toWinRTIIterable.swift new file mode 100644 index 00000000..c6fa7853 --- /dev/null +++ b/Support/Sources/WindowsRuntime/ProjectedTypes/WindowsFoundationCollections/Sequence+toWinRTIIterable.swift @@ -0,0 +1,50 @@ +extension Sequence { + /// Converts a Swift sequence to a WinRT IIterable. + /// - Parameter sequence: The sequence to convert. + /// - Returns: The converted IIterable. + public func toWinRTIIterable() -> WindowsFoundationCollections_IIterable { + SequenceIterable(self) + } +} + +fileprivate class SequenceIterable: WinRTExport, WindowsFoundationCollections_IIterableProtocol { + typealias T = S.Element + + private let sequence: S + + init(_ sequence: S) { + self.sequence = sequence + } + + func first() throws -> WindowsFoundationCollections_IIterator { + SequenceIterator(sequence.makeIterator()) + } +} + +internal class SequenceIterator: WinRTExport, WindowsFoundationCollections_IIteratorProtocol { + typealias T = I.Element + + private var iterator: I + private var current: T? + + init(_ iterator: I) { + self.iterator = iterator + self.current = self.iterator.next() + } + + func _hasCurrent() throws -> Bool { current != nil } + + func _current() throws -> T { + guard let current else { throw HResult.Error.illegalMethodCall } + return current + } + + func moveNext() throws -> Bool { + current = iterator.next() + return current != nil + } + + func getMany(_ items: [I.Element]) throws -> UInt32 { + throw HResult.Error.notImpl // TODO(#31): Implement out arrays + } +} \ No newline at end of file