Skip to content

Move platform requirements to availability annotations #348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
// swift-tools-version: 5.8

import PackageDescription
import CompilerPluginSupport

// Availability Macros
let availabilityTags = [_Availability("AsyncAlgorithms")]
let versionNumbers = ["1.0"]

// Availability Macro Utilities
enum _OSAvailability: String {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming nit: this doesn't need an underscore since this is not public API (it is restricted to the package)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack, this was copy-pasta from Foundation. I've fixed this up now.

// This should match the package's deployment target
case alwaysAvailable = "macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming nit: this should probably be more so named initialIntroduction and not the "alwaysAvailable" since this is not something that has always been around.

// Use 10000 for future availability to avoid compiler magic around
// the 9999 version number but ensure it is greater than 9999
case future = "macOS 10000, iOS 10000, tvOS 10000, watchOS 10000"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be good to have a "pending" case for the 9999 versions

}

struct _Availability {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto here on the underscore.

let name: String
let osAvailability: _OSAvailability

init(_ name: String, availability: _OSAvailability = .alwaysAvailable) {
self.name = name
self.osAvailability = availability
}
}

let availabilityMacros: [SwiftSetting] = versionNumbers.flatMap { version in
availabilityTags.map {
.enableExperimentalFeature("AvailabilityMacro=\($0.name) \(version):\($0.osAvailability.rawValue)")
}
}

let package = Package(
name: "swift-async-algorithms",
platforms: [
.macOS("10.15"),
.iOS("13.0"),
.tvOS("13.0"),
.watchOS("6.0"),
],
products: [
.library(name: "AsyncAlgorithms", targets: ["AsyncAlgorithms"])
],
Expand All @@ -20,29 +44,29 @@ let package = Package(
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "DequeModule", package: "swift-collections"),
],
swiftSettings: [
swiftSettings: availabilityMacros + [
.enableExperimentalFeature("StrictConcurrency=complete")
]
),
.target(
name: "AsyncSequenceValidation",
dependencies: ["_CAsyncSequenceValidationSupport", "AsyncAlgorithms"],
swiftSettings: [
swiftSettings: availabilityMacros + [
.enableExperimentalFeature("StrictConcurrency=complete")
]
),
.systemLibrary(name: "_CAsyncSequenceValidationSupport"),
.target(
name: "AsyncAlgorithms_XCTest",
dependencies: ["AsyncAlgorithms", "AsyncSequenceValidation"],
swiftSettings: [
swiftSettings: availabilityMacros + [
.enableExperimentalFeature("StrictConcurrency=complete")
]
),
.testTarget(
name: "AsyncAlgorithmsTests",
dependencies: ["AsyncAlgorithms", "AsyncSequenceValidation", "AsyncAlgorithms_XCTest"],
swiftSettings: [
swiftSettings: availabilityMacros + [
.enableExperimentalFeature("StrictConcurrency=complete")
]
),
Expand Down
4 changes: 4 additions & 0 deletions Sources/AsyncAlgorithms/AsyncAdjacentPairsSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//
//===----------------------------------------------------------------------===//

@available(AsyncAlgorithms 1.0, *)
extension AsyncSequence {
/// An `AsyncSequence` that iterates over the adjacent pairs of the original
/// original `AsyncSequence`.
Expand All @@ -26,6 +27,7 @@ extension AsyncSequence {
///
/// - Returns: An `AsyncSequence` where the element is a tuple of two adjacent elements
/// or the original `AsyncSequence`.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func adjacentPairs() -> AsyncAdjacentPairsSequence<Self> {
AsyncAdjacentPairsSequence(self)
Expand All @@ -34,6 +36,7 @@ extension AsyncSequence {

/// An `AsyncSequence` that iterates over the adjacent pairs of the original
/// `AsyncSequence`.
@available(AsyncAlgorithms 1.0, *)
@frozen
public struct AsyncAdjacentPairsSequence<Base: AsyncSequence>: AsyncSequence {
public typealias Element = (Base.Element, Base.Element)
Expand Down Expand Up @@ -83,6 +86,7 @@ public struct AsyncAdjacentPairsSequence<Base: AsyncSequence>: AsyncSequence {
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncAdjacentPairsSequence: Sendable where Base: Sendable, Base.Element: Sendable {}

@available(*, unavailable)
Expand Down
2 changes: 2 additions & 0 deletions Sources/AsyncAlgorithms/AsyncBufferedByteIterator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
/// }
///
///
@available(AsyncAlgorithms 1.0, *)
public struct AsyncBufferedByteIterator: AsyncIteratorProtocol {
public typealias Element = UInt8
@usableFromInline var buffer: _AsyncBytesBuffer
Expand Down Expand Up @@ -67,6 +68,7 @@ public struct AsyncBufferedByteIterator: AsyncIteratorProtocol {
@available(*, unavailable)
extension AsyncBufferedByteIterator: Sendable {}

@available(AsyncAlgorithms 1.0, *)
@frozen @usableFromInline
internal struct _AsyncBytesBuffer {
@usableFromInline
Expand Down
6 changes: 6 additions & 0 deletions Sources/AsyncAlgorithms/AsyncChain2Sequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/// - s2: The second asynchronous sequence.
/// - Returns: An asynchronous sequence that iterates first over the elements of `s1`, and
/// then over the elements of `s2`.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chain<Base1: AsyncSequence, Base2: AsyncSequence>(
_ s1: Base1,
Expand All @@ -26,6 +27,7 @@ public func chain<Base1: AsyncSequence, Base2: AsyncSequence>(
}

/// A concatenation of two asynchronous sequences with the same element type.
@available(AsyncAlgorithms 1.0, *)
@frozen
public struct AsyncChain2Sequence<Base1: AsyncSequence, Base2: AsyncSequence> where Base1.Element == Base2.Element {
@usableFromInline
Expand All @@ -41,10 +43,12 @@ public struct AsyncChain2Sequence<Base1: AsyncSequence, Base2: AsyncSequence> wh
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncChain2Sequence: AsyncSequence {
public typealias Element = Base1.Element

/// The iterator for a `AsyncChain2Sequence` instance.
@available(AsyncAlgorithms 1.0, *)
@frozen
public struct Iterator: AsyncIteratorProtocol {
@usableFromInline
Expand Down Expand Up @@ -76,12 +80,14 @@ extension AsyncChain2Sequence: AsyncSequence {
}
}

@available(AsyncAlgorithms 1.0, *)
@inlinable
public func makeAsyncIterator() -> Iterator {
Iterator(base1.makeAsyncIterator(), base2.makeAsyncIterator())
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncChain2Sequence: Sendable where Base1: Sendable, Base2: Sendable {}

@available(*, unavailable)
Expand Down
6 changes: 6 additions & 0 deletions Sources/AsyncAlgorithms/AsyncChain3Sequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
/// - s3: The third asynchronous sequence.
/// - Returns: An asynchronous sequence that iterates first over the elements of `s1`, and
/// then over the elements of `s2`, and then over the elements of `s3`
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chain<Base1: AsyncSequence, Base2: AsyncSequence, Base3: AsyncSequence>(
_ s1: Base1,
Expand All @@ -28,6 +29,7 @@ public func chain<Base1: AsyncSequence, Base2: AsyncSequence, Base3: AsyncSequen
}

/// A concatenation of three asynchronous sequences with the same element type.
@available(AsyncAlgorithms 1.0, *)
@frozen
public struct AsyncChain3Sequence<Base1: AsyncSequence, Base2: AsyncSequence, Base3: AsyncSequence>
where Base1.Element == Base2.Element, Base1.Element == Base3.Element {
Expand All @@ -48,10 +50,12 @@ where Base1.Element == Base2.Element, Base1.Element == Base3.Element {
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncChain3Sequence: AsyncSequence {
public typealias Element = Base1.Element

/// The iterator for a `AsyncChain3Sequence` instance.
@available(AsyncAlgorithms 1.0, *)
@frozen
public struct Iterator: AsyncIteratorProtocol {
@usableFromInline
Expand Down Expand Up @@ -93,12 +97,14 @@ extension AsyncChain3Sequence: AsyncSequence {
}
}

@available(AsyncAlgorithms 1.0, *)
@inlinable
public func makeAsyncIterator() -> Iterator {
Iterator(base1.makeAsyncIterator(), base2.makeAsyncIterator(), base3.makeAsyncIterator())
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncChain3Sequence: Sendable where Base1: Sendable, Base2: Sendable, Base3: Sendable {}

@available(*, unavailable)
Expand Down
5 changes: 5 additions & 0 deletions Sources/AsyncAlgorithms/AsyncChunkedByGroupSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
//
//===----------------------------------------------------------------------===//

@available(AsyncAlgorithms 1.0, *)
extension AsyncSequence {
/// Creates an asynchronous sequence that creates chunks of a given `RangeReplaceableCollection`
/// type by testing if elements belong in the same group.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chunked<Collected: RangeReplaceableCollection>(
into: Collected.Type,
Expand All @@ -21,6 +23,7 @@ extension AsyncSequence {
}

/// Creates an asynchronous sequence that creates chunks by testing if elements belong in the same group.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chunked(
by belongInSameGroup: @escaping @Sendable (Element, Element) -> Bool
Expand Down Expand Up @@ -51,6 +54,7 @@ extension AsyncSequence {
/// // [10, 20, 30]
/// // [10, 40, 40]
/// // [10, 20]
@available(AsyncAlgorithms 1.0, *)
public struct AsyncChunkedByGroupSequence<Base: AsyncSequence, Collected: RangeReplaceableCollection>: AsyncSequence
where Collected.Element == Base.Element {
public typealias Element = Collected
Expand Down Expand Up @@ -121,6 +125,7 @@ where Collected.Element == Base.Element {
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncChunkedByGroupSequence: Sendable where Base: Sendable, Base.Element: Sendable {}

@available(*, unavailable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
//
//===----------------------------------------------------------------------===//

@available(AsyncAlgorithms 1.0, *)
extension AsyncSequence {
/// Creates an asynchronous sequence that creates chunks of a given `RangeReplaceableCollection` type on the uniqueness of a given subject.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chunked<Subject: Equatable, Collected: RangeReplaceableCollection>(
into: Collected.Type,
Expand All @@ -20,6 +22,7 @@ extension AsyncSequence {
}

/// Creates an asynchronous sequence that creates chunks on the uniqueness of a given subject.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chunked<Subject: Equatable>(
on projection: @escaping @Sendable (Element) -> Subject
Expand All @@ -29,6 +32,7 @@ extension AsyncSequence {
}

/// An `AsyncSequence` that chunks on a subject when it differs from the last element.
@available(AsyncAlgorithms 1.0, *)
public struct AsyncChunkedOnProjectionSequence<
Base: AsyncSequence,
Subject: Equatable,
Expand Down Expand Up @@ -104,6 +108,7 @@ public struct AsyncChunkedOnProjectionSequence<
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncChunkedOnProjectionSequence: Sendable where Base: Sendable, Base.Element: Sendable {}

@available(*, unavailable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
//
//===----------------------------------------------------------------------===//

@available(AsyncAlgorithms 1.0, *)
extension AsyncSequence {
/// Creates an asynchronous sequence that creates chunks of a given `RangeReplaceableCollection` type of a given count or when a signal `AsyncSequence` produces an element.
@available(AsyncAlgorithms 1.0, *)
public func chunks<Signal, Collected: RangeReplaceableCollection>(
ofCount count: Int,
or signal: Signal,
Expand All @@ -20,6 +22,7 @@ extension AsyncSequence {
}

/// Creates an asynchronous sequence that creates chunks of a given count or when a signal `AsyncSequence` produces an element.
@available(AsyncAlgorithms 1.0, *)
public func chunks<Signal>(
ofCount count: Int,
or signal: Signal
Expand All @@ -28,6 +31,7 @@ extension AsyncSequence {
}

/// Creates an asynchronous sequence that creates chunks of a given `RangeReplaceableCollection` type when a signal `AsyncSequence` produces an element.
@available(AsyncAlgorithms 1.0, *)
public func chunked<Signal, Collected: RangeReplaceableCollection>(
by signal: Signal,
into: Collected.Type
Expand All @@ -36,6 +40,7 @@ extension AsyncSequence {
}

/// Creates an asynchronous sequence that creates chunks when a signal `AsyncSequence` produces an element.
@available(AsyncAlgorithms 1.0, *)
public func chunked<Signal>(by signal: Signal) -> AsyncChunksOfCountOrSignalSequence<Self, [Element], Signal> {
chunked(by: signal, into: [Element].self)
}
Expand Down Expand Up @@ -78,6 +83,7 @@ extension AsyncSequence {
}

/// An `AsyncSequence` that chunks elements into collected `RangeReplaceableCollection` instances by either count or a signal from another `AsyncSequence`.
@available(AsyncAlgorithms 1.0, *)
public struct AsyncChunksOfCountOrSignalSequence<
Base: AsyncSequence,
Collected: RangeReplaceableCollection,
Expand Down
9 changes: 6 additions & 3 deletions Sources/AsyncAlgorithms/AsyncChunksOfCountSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
//
//===----------------------------------------------------------------------===//

@available(AsyncAlgorithms 1.0, *)
extension AsyncSequence {
/// Creates an asynchronous sequence that creates chunks of a given `RangeReplaceableCollection` of a given count.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chunks<Collected: RangeReplaceableCollection>(
ofCount count: Int,
Expand All @@ -20,13 +22,15 @@ extension AsyncSequence {
}

/// Creates an asynchronous sequence that creates chunks of a given count.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func chunks(ofCount count: Int) -> AsyncChunksOfCountSequence<Self, [Element]> {
chunks(ofCount: count, into: [Element].self)
}
}

/// An `AsyncSequence` that chunks elements into `RangeReplaceableCollection` instances of at least a given count.
@available(AsyncAlgorithms 1.0, *)
public struct AsyncChunksOfCountSequence<Base: AsyncSequence, Collected: RangeReplaceableCollection>: AsyncSequence
where Collected.Element == Base.Element {
public typealias Element = Collected
Expand Down Expand Up @@ -89,8 +93,7 @@ where Collected.Element == Base.Element {
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncChunksOfCountSequence: Sendable where Base: Sendable, Base.Element: Sendable {}
@available(AsyncAlgorithms 1.0, *)
extension AsyncChunksOfCountSequence.Iterator: Sendable where Base.AsyncIterator: Sendable, Base.Element: Sendable {}

@available(*, unavailable)
extension AsyncChunksOfCountSequence.Iterator: Sendable {}
4 changes: 4 additions & 0 deletions Sources/AsyncAlgorithms/AsyncCompactedSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//
//===----------------------------------------------------------------------===//

@available(AsyncAlgorithms 1.0, *)
extension AsyncSequence {
/// Returns a new `AsyncSequence` that iterates over every non-nil element from the
/// original `AsyncSequence`.
Expand All @@ -18,6 +19,7 @@ extension AsyncSequence {
/// - Returns: An `AsyncSequence` where the element is the unwrapped original
/// element and iterates over every non-nil element from the original
/// `AsyncSequence`.
@available(AsyncAlgorithms 1.0, *)
@inlinable
public func compacted<Unwrapped>() -> AsyncCompactedSequence<Self, Unwrapped>
where Element == Unwrapped? {
Expand All @@ -27,6 +29,7 @@ extension AsyncSequence {

/// An `AsyncSequence` that iterates over every non-nil element from the original
/// `AsyncSequence`.
@available(AsyncAlgorithms 1.0, *)
@frozen
public struct AsyncCompactedSequence<Base: AsyncSequence, Element>: AsyncSequence
where Base.Element == Element? {
Expand Down Expand Up @@ -66,6 +69,7 @@ where Base.Element == Element? {
}
}

@available(AsyncAlgorithms 1.0, *)
extension AsyncCompactedSequence: Sendable where Base: Sendable, Base.Element: Sendable {}

@available(*, unavailable)
Expand Down
Loading
Loading