diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 189f232..bea2a11 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -5,6 +5,10 @@ on: branches: - main +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: ${{ github.ref_name != 'main' }} + jobs: API-Check: name: Diagnose API Breaking Changes @@ -12,7 +16,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Mark Workspace As Safe diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml new file mode 100644 index 0000000..abcc945 --- /dev/null +++ b/.github/workflows/compatibility.yml @@ -0,0 +1,230 @@ +name: Swift Version Compatibility + +on: + pull_request: + branches: + - main + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: ${{ github.ref_name != 'main' }} + +jobs: + Build-Release-6-0: + name: Build Swift 6.0 Release + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.0" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration release + + Build-Debug-6-0: + name: Build Swift 6.0 Debug + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.0" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration debug + + Build-Release-6-1: + name: Build Swift 6.1 Release + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.1" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration release + + Build-Debug-6-1: + name: Build Swift 6.1 Debug + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.1" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration debug + + Build-Release-6-2: + name: Build Swift 6.2 Release + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.2" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration release + + Build-Debug-6-2: + name: Build Swift 6.2 Debug + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.2" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration debug + + Build-Release-6-3: + name: Build Swift 6.3 Release + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.3" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration release + + Build-Debug-6-3: + name: Build Swift 6.3 Debug + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.3" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration debug + + Build-Release-6-4: + name: Build Swift 6.4 Release + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.4.x-snapshot" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration release + + Build-Debug-6-4: + name: Build Swift 6.4 Debug + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "6.4.x-snapshot" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration debug + + Build-Release-main: + name: Build Swift main Release + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "main-snapshot" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration release + + Build-Debug-main: + name: Build Swift main Debug + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Source + uses: actions/checkout@v6 + - name: Set Swift Version + uses: vapor/swiftly-action@v0.2 + with: + toolchain: "main-snapshot" + - name: Swift Version + run: | + swift --version + - name: Build + run: | + swift build --configuration debug diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3a0c60..c1a62c1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,13 @@ on: pull_request: branches: - main + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: ${{ github.ref_name != 'main' }} jobs: Test-Release: @@ -11,7 +18,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Swift Version run: | swift --version @@ -27,7 +34,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Swift Version run: | swift --version diff --git a/Package.swift b/Package.swift index 1e7d6e3..fbbefc2 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.9 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. // @@ -13,6 +13,14 @@ import PackageDescription +let swiftSettings: [PackageDescription.SwiftSetting] = [ + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("InternalImportsByDefault"), + .enableUpcomingFeature("MemberImportVisibility"), + .enableUpcomingFeature("InferIsolatedConformances"), + .enableUpcomingFeature("ImmutableWeakCaptures"), +] + let package = Package( name: "CodableDatastore", platforms: [ @@ -28,8 +36,8 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/mochidev/AsyncSequenceReader.git", .upToNextMinor(from: "0.3.1")), - .package(url: "https://github.com/mochidev/Bytes.git", .upToNextMinor(from: "0.3.0")), + .package(url: "https://github.com/mochidev/AsyncSequenceReader.git", .upToNextMinor(from: "0.5.0")), + .package(url: "https://github.com/mochidev/Bytes.git", .upToNextMinor(from: "0.6.2")), ], targets: [ .target( @@ -38,16 +46,12 @@ let package = Package( "AsyncSequenceReader", "Bytes" ], - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency"), - ] + swiftSettings: swiftSettings ), .testTarget( name: "CodableDatastoreTests", dependencies: ["CodableDatastore"], - swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency"), - ] + swiftSettings: swiftSettings ), ] ) diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift deleted file mode 100644 index a3be699..0000000 --- a/Package@swift-6.0.swift +++ /dev/null @@ -1,47 +0,0 @@ -// swift-tools-version: 6.0 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -// -// Package@swift-6.0.swift -// https://github.com/mochidev/CodableDatastore -// -// Created by Dimitri Bouniol on 2024-06-19. -// Copyright © 2023-26 Mochi Development, Inc. All rights reserved. -// mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 -// - - -import PackageDescription - -let package = Package( - name: "CodableDatastore", - platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6), - ], - products: [ - .library( - name: "CodableDatastore", - targets: ["CodableDatastore"] - ), - ], - dependencies: [ - .package(url: "https://github.com/mochidev/AsyncSequenceReader.git", .upToNextMinor(from: "0.3.1")), - .package(url: "https://github.com/mochidev/Bytes.git", .upToNextMinor(from: "0.3.0")), - ], - targets: [ - .target( - name: "CodableDatastore", - dependencies: [ - "AsyncSequenceReader", - "Bytes" - ] - ), - .testTarget( - name: "CodableDatastoreTests", - dependencies: ["CodableDatastore"] - ), - ] -) diff --git a/Sources/CodableDatastore/Datastore/AsyncInstances.swift b/Sources/CodableDatastore/Datastore/AsyncInstances.swift index 26ba40b..1d1c191 100644 --- a/Sources/CodableDatastore/Datastore/AsyncInstances.swift +++ b/Sources/CodableDatastore/Datastore/AsyncInstances.swift @@ -7,7 +7,11 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // +#if compiler(<6.1) +public protocol AsyncInstances: AsyncSequence {} +#else public protocol AsyncInstances: AsyncSequence, Sendable {} +#endif extension AsyncInstances { /// Returns the first instance of the sequence, if it exists. @@ -62,3 +66,12 @@ extension AsyncCompactMapSequence: AsyncInstances {} extension AsyncThrowingCompactMapSequence: AsyncInstances {} extension AsyncFilterSequence: AsyncInstances {} extension AsyncThrowingFilterSequence: AsyncInstances {} + +#if compiler(<6.1) +extension AsyncMapSequence: @unchecked Sendable where Base : Sendable, Transformed : Sendable, Base.Element : Sendable {} +extension AsyncThrowingMapSequence: @unchecked Sendable where Base : Sendable, Transformed : Sendable, Base.Element : Sendable {} +extension AsyncCompactMapSequence: @unchecked Sendable where Base : Sendable, ElementOfResult : Sendable, Base.Element : Sendable {} +extension AsyncThrowingCompactMapSequence: @unchecked Sendable where Base : Sendable, Base.Element : Sendable {} +extension AsyncFilterSequence: @unchecked Sendable where Base : Sendable, Base.Element : Sendable {} +extension AsyncThrowingFilterSequence: @unchecked Sendable where Base : Sendable, Base.Element : Sendable {} +#endif diff --git a/Sources/CodableDatastore/Datastore/Datastore.swift b/Sources/CodableDatastore/Datastore/Datastore.swift index 2f90a53..a16410b 100644 --- a/Sources/CodableDatastore/Datastore/Datastore.swift +++ b/Sources/CodableDatastore/Datastore/Datastore.swift @@ -7,11 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -#if canImport(Darwin) -import Foundation -#else -@preconcurrency import Foundation -#endif +public import Foundation /// A store for a homogenous collection of instances. public actor Datastore: Sendable { @@ -177,7 +173,7 @@ extension Datastore { } } - func registerAndMigrate(with transaction: DatastoreInterfaceProtocol) async throws -> Progress { + func registerAndMigrate(with transaction: any DatastoreInterfaceProtocol) async throws -> Progress { let persistedDescriptor = try await transaction.register(datastore: self) /// Only operate on read-write datastores beyond this point. @@ -1680,6 +1676,6 @@ extension Datastore where InstanceType: Identifiable, IdentifierType == Instance private enum TaskStatus { case waiting - case inProgress(Task) + case inProgress(Task) case complete(Value) } diff --git a/Sources/CodableDatastore/Datastore/DatastoreDescriptor.swift b/Sources/CodableDatastore/Datastore/DatastoreDescriptor.swift index a55163b..b31d05d 100644 --- a/Sources/CodableDatastore/Datastore/DatastoreDescriptor.swift +++ b/Sources/CodableDatastore/Datastore/DatastoreDescriptor.swift @@ -7,7 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation +public import Foundation /// A description of a ``Datastore``'s requirements of a persistence. /// diff --git a/Sources/CodableDatastore/Datastore/DatastoreError.swift b/Sources/CodableDatastore/Datastore/DatastoreError.swift index 22bea19..56411e2 100644 --- a/Sources/CodableDatastore/Datastore/DatastoreError.swift +++ b/Sources/CodableDatastore/Datastore/DatastoreError.swift @@ -7,7 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation +public import Foundation /// A ``Datastore``-specific error. public enum DatastoreError: LocalizedError { diff --git a/Sources/CodableDatastore/Datastore/DatastoreKey.swift b/Sources/CodableDatastore/Datastore/DatastoreKey.swift index 0cf93e9..e01e4f8 100644 --- a/Sources/CodableDatastore/Datastore/DatastoreKey.swift +++ b/Sources/CodableDatastore/Datastore/DatastoreKey.swift @@ -30,14 +30,14 @@ extension DatastoreKey: ExpressibleByStringLiteral { } extension DatastoreKey: Decodable { - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() self.init(rawValue: try container.decode(String.self)) } } extension DatastoreKey: Encodable { - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() try container.encode(rawValue) } diff --git a/Sources/CodableDatastore/Indexes/IndexName.swift b/Sources/CodableDatastore/Indexes/IndexName.swift index 6508440..fcd3b32 100644 --- a/Sources/CodableDatastore/Indexes/IndexName.swift +++ b/Sources/CodableDatastore/Indexes/IndexName.swift @@ -31,14 +31,14 @@ extension IndexName: ExpressibleByStringLiteral { } extension IndexName: Decodable { - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() self.init(rawValue: try container.decode(String.self)) } } extension IndexName: Encodable { - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() try container.encode(rawValue) } diff --git a/Sources/CodableDatastore/Indexes/IndexRangeExpression.swift b/Sources/CodableDatastore/Indexes/IndexRangeExpression.swift index c12e9be..62b9b75 100644 --- a/Sources/CodableDatastore/Indexes/IndexRangeExpression.swift +++ b/Sources/CodableDatastore/Indexes/IndexRangeExpression.swift @@ -37,6 +37,21 @@ public enum RangeOrder: Equatable, Sendable { } } +#if compiler(<6.1) +/// A type that can represent a range within an index. +public protocol IndexRangeExpression { + associatedtype Bound: Comparable + + /// The definition of the lower bound of the range. + var lowerBoundExpression: RangeBoundExpression { get } + + /// The definition of the upper bound of the range. + var upperBoundExpression: RangeBoundExpression { get } + + ///The order the elements in the range appear. + var order: RangeOrder { get } +} +#else /// A type that can represent a range within an index. public protocol IndexRangeExpression: Sendable { associatedtype Bound: Comparable & Sendable @@ -50,6 +65,7 @@ public protocol IndexRangeExpression: Sendable { ///The order the elements in the range appear. var order: RangeOrder { get } } +#endif extension IndexRangeExpression { /// Reverse a range so it is iterated on in the opposite direction. @@ -155,6 +171,14 @@ extension PartialRangeFrom: IndexRangeExpression { public var order: RangeOrder { .ascending } } +#if compiler(<6.2) +extension Range: @unchecked Sendable where Bound: Sendable {} +extension ClosedRange: @unchecked Sendable where Bound: Sendable {} +extension PartialRangeUpTo: @unchecked Sendable where Bound: Sendable {} +extension PartialRangeThrough: @unchecked Sendable where Bound: Sendable {} +extension PartialRangeFrom: @unchecked Sendable where Bound: Sendable {} +#endif + /// A range of indices within an Index to fetch. public struct IndexRange: IndexRangeExpression { /// The lower bound of the range. diff --git a/Sources/CodableDatastore/Indexes/IndexRepresentation.swift b/Sources/CodableDatastore/Indexes/IndexRepresentation.swift index 187e194..ed1a755 100644 --- a/Sources/CodableDatastore/Indexes/IndexRepresentation.swift +++ b/Sources/CodableDatastore/Indexes/IndexRepresentation.swift @@ -276,9 +276,5 @@ public struct AnyIndexRepresentation: Hashable, Sendable { } } -/// Forced KeyPath conformance since Swift 5.10 doesn't support it out of the box. -#if compiler(>=6) +/// Forced KeyPath conformance since Swift 6 doesn't support it out of the box. extension KeyPath: @unchecked @retroactive Sendable where Root: Sendable, Value: Sendable {} -#else -extension KeyPath: @unchecked Sendable where Root: Sendable, Value: Sendable {} -#endif diff --git a/Sources/CodableDatastore/Indexes/IndexType.swift b/Sources/CodableDatastore/Indexes/IndexType.swift index 71b7fce..1ad06ee 100644 --- a/Sources/CodableDatastore/Indexes/IndexType.swift +++ b/Sources/CodableDatastore/Indexes/IndexType.swift @@ -35,14 +35,14 @@ extension IndexType: ExpressibleByStringLiteral { } extension IndexType: Decodable { - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { let container = try decoder.singleValueContainer() self.init(rawValue: try container.decode(String.self)) } } extension IndexType: Encodable { - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.singleValueContainer() try container.encode(rawValue) } diff --git a/Sources/CodableDatastore/Indexes/Indexable.swift b/Sources/CodableDatastore/Indexes/Indexable.swift index 0bd623a..329bb72 100644 --- a/Sources/CodableDatastore/Indexes/Indexable.swift +++ b/Sources/CodableDatastore/Indexes/Indexable.swift @@ -7,7 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation +public import Foundation /// An alias representing the requirements for a property to be indexable, namely that they conform to both ``/Swift/Codable`` and ``/Swift/Comparable``. public typealias Indexable = Comparable & Hashable & Codable & Sendable @@ -25,7 +25,6 @@ public struct AnyIndexable { #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) /// Matching implementation from https://github.com/apple/swift/pull/64899/files -#if compiler(>=6) extension Never: @retroactive Codable { public init(from decoder: any Decoder) throws { let context = DecodingError.Context( @@ -35,17 +34,6 @@ extension Never: @retroactive Codable { } public func encode(to encoder: any Encoder) throws {} } -#else -extension Never: Codable { - public init(from decoder: any Decoder) throws { - let context = DecodingError.Context( - codingPath: decoder.codingPath, - debugDescription: "Unable to decode an instance of Never.") - throw DecodingError.typeMismatch(Never.self, context) - } - public func encode(to encoder: any Encoder) throws {} -} -#endif #endif /// A marker protocol for types that can be used as a ranged index. @@ -92,21 +80,12 @@ extension UInt16: DiscreteIndexable, RangedIndexable {} extension UInt32: DiscreteIndexable, RangedIndexable {} extension UInt64: DiscreteIndexable, RangedIndexable {} -#if compiler(>=6) extension Optional: @retroactive Comparable where Wrapped: Comparable { public static func < (lhs: Self, rhs: Self) -> Bool { if let lhs, let rhs { return lhs < rhs } return lhs == nil && rhs != nil } } -#else -extension Optional: Comparable where Wrapped: Comparable { - public static func < (lhs: Self, rhs: Self) -> Bool { - if let lhs, let rhs { return lhs < rhs } - return lhs == nil && rhs != nil - } -} -#endif extension Optional: DiscreteIndexable where Wrapped: DiscreteIndexable {} extension Optional: RangedIndexable where Wrapped: RangedIndexable {} diff --git a/Sources/CodableDatastore/Indexes/UUID+Comparable.swift b/Sources/CodableDatastore/Indexes/UUID+Comparable.swift index 5a34e42..3f31752 100644 --- a/Sources/CodableDatastore/Indexes/UUID+Comparable.swift +++ b/Sources/CodableDatastore/Indexes/UUID+Comparable.swift @@ -7,12 +7,10 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation +#if !canImport(FoundationEssentials) +public import Foundation /// Make UUIDs comparable on platforms that shipped without it, so that they can be used transparently as an index. -#if !canImport(FoundationEssentials) -#if swift(<5.9) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(Linux) || os(Windows) -#if compiler(>=6) extension UUID: @retroactive Comparable { @inlinable @_disfavoredOverload @@ -20,17 +18,6 @@ extension UUID: @retroactive Comparable { lhs.uuid < rhs.uuid } } -#else -extension UUID: Comparable { - @inlinable - @_disfavoredOverload - public static func < (lhs: UUID, rhs: UUID) -> Bool { - lhs.uuid < rhs.uuid - } -} -#endif -#endif -#endif /// Make UUIDs comparable, so that they can be used transparently as an index. /// @@ -57,3 +44,4 @@ public func < (lhs: uuid_t, rhs: uuid_t) -> Bool { return result < 0 } +#endif diff --git a/Sources/CodableDatastore/Persistence/DatastoreInterfaceError.swift b/Sources/CodableDatastore/Persistence/DatastoreInterfaceError.swift index 0295a26..13bb8c9 100644 --- a/Sources/CodableDatastore/Persistence/DatastoreInterfaceError.swift +++ b/Sources/CodableDatastore/Persistence/DatastoreInterfaceError.swift @@ -7,7 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation +public import Foundation /// An error that may be returned from ``DatastoreInterfaceProtocol`` methods. public enum DatastoreInterfaceError: LocalizedError { diff --git a/Sources/CodableDatastore/Persistence/DatastoreInterfaceProtocol.swift b/Sources/CodableDatastore/Persistence/DatastoreInterfaceProtocol.swift index 6f1b35d..da24530 100644 --- a/Sources/CodableDatastore/Persistence/DatastoreInterfaceProtocol.swift +++ b/Sources/CodableDatastore/Persistence/DatastoreInterfaceProtocol.swift @@ -7,7 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation +public import Foundation /// A interface a ``Datastore`` uses to communicate with a ``Persistence``. /// diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/AsyncThrowingBackpressureStream.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/AsyncThrowingBackpressureStream.swift index 2bcde04..e584b53 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/AsyncThrowingBackpressureStream.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/AsyncThrowingBackpressureStream.swift @@ -18,11 +18,11 @@ import Foundation /// The reading task may be cancelled at any time, immediately ending the loop, and propagaing the cancellation to the writing child task, stopping any more values from being provided to the stream. struct AsyncThrowingBackpressureStream: Sendable { fileprivate actor StateMachine { - var pendingWriteEvents: [(CheckedContinuation, Result)] = [] - var pendingReadContinuation: CheckedContinuation? + var pendingWriteEvents: [(CheckedContinuation, Result)] = [] + var pendingReadContinuation: CheckedContinuation? var wasCancelled = false - func provide(_ result: Result, in continuation: CheckedContinuation) { + func provide(_ result: Result, in continuation: CheckedContinuation) { /// If reads were cancelled, propagate the cancellation to the provider without saving the result. guard !wasCancelled else { continuation.resume(throwing: CancellationError()) @@ -128,7 +128,7 @@ struct AsyncThrowingBackpressureStream: Sendable { } } - fileprivate func finish(throwing error: Error? = nil) async throws { + fileprivate func finish(throwing error: (any Error)? = nil) async throws { try await withCheckedThrowingContinuation { continuation in guard let stateMachine else { continuation.resume(throwing: CancellationError()) return diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndex.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndex.swift index e961503..4dd5bb0 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndex.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndex.swift @@ -23,9 +23,9 @@ extension DiskPersistence.Datastore { let id: PersistenceDatastoreIndexID var _manifest: DatastoreIndexManifest? - var manifestTask: Task? + var manifestTask: Task? - var cachedOrderedPages: Task<[LazyTask?], Error>? + var cachedOrderedPages: Task<[LazyTask?], any Error>? var isPersisted: Bool diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndexManifest.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndexManifest.swift index b927997..ffddbd4 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndexManifest.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastoreIndexManifest.swift @@ -7,9 +7,9 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation import AsyncSequenceReader import Bytes +import Foundation typealias DatastoreIndexManifestIdentifier = DatedIdentifier @@ -102,7 +102,7 @@ extension DatastoreIndexManifest { #endif } - init(sequence: AnyReadableSequence, id: ID) async throws { + init(sequence: AnyReadableSequence, id: ID) async throws { self.id = id var iterator = sequence.makeAsyncIterator() diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePage.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePage.swift index 72ac053..f1bf280 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePage.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePage.swift @@ -7,9 +7,9 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation import AsyncSequenceReader import Bytes +import Foundation typealias DatastorePageIdentifier = DatedIdentifier.Datastore.Page> @@ -19,7 +19,7 @@ extension DiskPersistence.Datastore { let id: PersistenceDatastorePageID - var blocksReaderTask: Task>, Error>? + var blocksReaderTask: Task>, any Error>? var isPersisted: Bool @@ -88,7 +88,7 @@ extension DiskPersistence.Datastore.Page { // MARK: - Persistence extension DiskPersistence.Datastore.Page { - private var readableSequence: AnyReadableSequence { + private var readableSequence: AnyReadableSequence { get throws { #if canImport(Darwin) if #available(macOS 12.0, iOS 15, watchOS 8, tvOS 15, *) { @@ -102,17 +102,29 @@ extension DiskPersistence.Datastore.Page { } } - private nonisolated func performRead(sequence: AnyReadableSequence) async throws -> MultiplexedAsyncSequence> { - var iterator = sequence.makeAsyncIterator() + private nonisolated func performRead(sequence: AnyReadableSequence) async throws -> MultiplexedAsyncSequence> { + var iterator = sequence.makeBufferedIterator() try await iterator.check(Self.header) /// Pages larger than 1 GB are unsupported. - let transformation = try await iterator.collect(max: Configuration.maximumPageSize) { sequence in + let transformation = await iterator.collect(max: Configuration.maximumPageSize) { sequence in sequence.iteratorMap { iterator in + #if canImport(Darwin) + if #available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) { + guard let block = try await iterator.next(DatastorePageEntryBlock.self, isolation: #isolation) + else { throw DiskPersistenceError.invalidPageFormat } + return block + } else { + guard let block = try await iterator.next(DatastorePageEntryBlock.self) + else { throw DiskPersistenceError.invalidPageFormat } + return block + } + #else guard let block = try await iterator.next(DatastorePageEntryBlock.self) else { throw DiskPersistenceError.invalidPageFormat } return block + #endif } } @@ -123,7 +135,7 @@ extension DiskPersistence.Datastore.Page { } } - var blocks: MultiplexedAsyncSequence> { + var blocks: MultiplexedAsyncSequence> { get async throws { if let blocksReaderTask { return try await blocksReaderTask.value @@ -162,7 +174,7 @@ extension DiskPersistence.Datastore.Page { actor MultiplexedAsyncSequence: AsyncSequence where Base.Element: Sendable, Base.AsyncIterator: Sendable, Base.AsyncIterator.Element: Sendable { typealias Element = Base.Element - private var cachedEntries: [Task] = [] + private var cachedEntries: [Task] = [] private var baseIterator: Base.AsyncIterator? struct AsyncIterator: AsyncIteratorProtocol & Sendable { @@ -184,7 +196,7 @@ actor MultiplexedAsyncSequence: AsyncSequence wh precondition(index == cachedEntries.count, "\(index) is out of bounds.") - let lastTask: Task? = cachedEntries.last + let lastTask: Task? = cachedEntries.last let newTask = Task { /// Make sure previous iteration finished before sourcing the next one. @@ -218,7 +230,7 @@ actor MultiplexedAsyncSequence: AsyncSequence wh } extension RangeReplaceableCollection where Self: Sendable { - init(_ sequence: S) async throws where S.Element == Element { + init(_ sequence: sending S) async throws where S.Element == Element { self = try await sequence.reduce(into: Self.init()) { @Sendable partialResult, element in partialResult.append(element) } diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntry.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntry.swift index 1edbd6f..4ac0810 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntry.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntry.swift @@ -7,9 +7,9 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation import AsyncSequenceReader import Bytes +import Foundation struct DatastorePageEntry: Hashable { var headers: [Bytes] diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntryBlock.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntryBlock.swift index 5d9f90e..7c08a53 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntryBlock.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePageEntryBlock.swift @@ -7,9 +7,9 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation import AsyncSequenceReader -import Bytes +public import Bytes +import Foundation /// A block of data that represents a portion of an entry on a page. @usableFromInline @@ -35,7 +35,12 @@ enum DatastorePageEntryBlock: Hashable, Sendable { // MARK: - Decoding +#if canImport(Darwin) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension AsyncIteratorProtocol where Element == Byte { + #if compiler(>=6.2) + @concurrent + #endif @usableFromInline mutating func next(_ type: DatastorePageEntryBlock.Type) async throws -> DatastorePageEntryBlock? { guard let blockType = try await nextIfPresent(utf8: String.self, count: 1) else { @@ -70,6 +75,47 @@ extension AsyncIteratorProtocol where Element == Byte { } } } +#endif // canImport(Darwin) + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +extension AsyncIteratorProtocol where Element == Byte { + @usableFromInline + mutating func next( + _ type: DatastorePageEntryBlock.Type, + isolation actor: isolated (any Actor)? = #isolation + ) async throws -> DatastorePageEntryBlock? { + guard let blockType = try await nextIfPresent(utf8: String.self, count: 1) else { + return nil + } + + /// Fail early if the block type is not supported. + switch blockType { + case "<", "=", ">", "~": break + default: + throw DiskPersistenceError.invalidPageFormat + } + + /// Artificially limit ourselves to ~ 9 GB, though realistically our limit will be much, much lower. + guard let blockSizeBytes = try await collect(upToIncluding: "\n".utf8Bytes, throwsIfOver: 11) + else { throw DiskPersistenceError.invalidPageFormat } + + let decimalSizeString = String(utf8Bytes: blockSizeBytes.dropLast(1)) + guard let blockSize = Int(decimalSizeString), blockSize > 0 + else { throw DiskPersistenceError.invalidPageFormat } + + let payload = try await next(Bytes.self, count: blockSize) + + try await check(utf8: "\n") + + switch blockType { + case "<": return .tail(payload) + case "=": return .complete(payload) + case ">": return .head(payload) + case "~": return .slice(payload) + default: throw DiskPersistenceError.invalidPageFormat + } + } +} // MARK: - Encoding diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/PersistenceDatastore.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/PersistenceDatastore.swift index 8d96cbe..45f22b9 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/PersistenceDatastore.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/PersistenceDatastore.swift @@ -27,7 +27,7 @@ extension DiskPersistence { var cachedRootObject: DatastoreRootManifest? - var lastUpdateDescriptorTask: Task? + var lastUpdateDescriptorTask: Task? /// The root objects that are being tracked in memory. var trackedRootObjects: [RootObject.ID : WeakValue] = [:] diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistence.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistence.swift index 78a3b26..58ae78f 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistence.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistence.swift @@ -7,11 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -#if canImport(Darwin) -import Foundation -#else -@preconcurrency import Foundation -#endif +public import Foundation public actor DiskPersistence: Persistence { /// The location of this persistence. @@ -21,7 +17,7 @@ public actor DiskPersistence: Persistence { var cachedStoreInfo: StoreInfo? /// A pointer to the last store info updater, so updates can be serialized after the last request - var lastUpdateStoreInfoTask: Task? + var lastUpdateStoreInfoTask: Task? /// The loaded Snapshots var snapshots: [SnapshotIdentifier: Snapshot] = [:] @@ -179,7 +175,7 @@ extension DiskPersistence { /// - Returns: A ``/Swift/Task`` which contains the value of the updater upon completion. func updateStoreInfo( @_inheritActorContext updater: @Sendable @escaping (_ storeInfo: inout StoreInfo) async throws -> T - ) -> Task where AccessMode == ReadWrite { + ) -> Task where AccessMode == ReadWrite { if let storeInfo = DiskPersistenceTaskLocals.storeInfo(for: self) { return Task { var updatedStoreInfo = storeInfo @@ -227,7 +223,7 @@ extension DiskPersistence { /// - Returns: A ``/Swift/Task`` which contains the value of the updater upon completion. func updateStoreInfo( @_inheritActorContext accessor: @Sendable @escaping (_ storeInfo: StoreInfo) async throws -> T - ) -> Task { + ) -> Task { if let storeInfo = DiskPersistenceTaskLocals.storeInfo(for: self) { return Task { try await accessor(storeInfo) } } @@ -319,7 +315,7 @@ extension DiskPersistence { func updateCurrentSnapshot( dateUpdate: ModificationUpdate = .updateOnWrite, updater: @escaping (_ snapshot: Snapshot) async throws -> T - ) -> Task where AccessMode == ReadWrite { + ) -> Task where AccessMode == ReadWrite { /// Grab access to the store info to load and update it. return updateStoreInfo { storeInfo in /// Grab the current snapshot from the store info @@ -345,7 +341,7 @@ extension DiskPersistence { /// - Returns: A ``/Swift/Task`` which contains the value of the updater upon completion. func updateCurrentSnapshot( accessor: @escaping (_ snapshot: Snapshot) async throws -> T - ) -> Task { + ) -> Task { /// Grab access to the store info to load and update it. return updateStoreInfo { storeInfo in /// Grab the current snapshot from the store info @@ -553,7 +549,7 @@ extension DiskPersistence { public func _withTransaction( actionName: String?, options: UnsafeTransactionOptions, - transaction: @Sendable (_ transaction: DatastoreInterfaceProtocol, _ isDurable: Bool) async throws -> T + transaction: @Sendable (_ transaction: any DatastoreInterfaceProtocol, _ isDurable: Bool) async throws -> T ) async throws -> T { try await withoutActuallyEscaping(transaction) { escapingTransaction in /// If the transaction is starting in the context of another persistence's transaction, make sure it is a read-only one. Otherwise assert and throw an error as it likely indicates a mistake and could lead to unexpected consistency violations if one persistence succeeds while the other fails. diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistenceError.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistenceError.swift index 13c931c..37e70c0 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistenceError.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/DiskPersistenceError.swift @@ -7,7 +7,7 @@ // mochidev-codable-datastore: 8A3D87799CB24B2BA7A7661369B88325 // -import Foundation +public import Foundation /// A ``DiskPersistence``-specific error. public enum DiskPersistenceError: LocalizedError, Equatable { diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/ISO8601DateFormatter+Milliseconds.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/ISO8601DateFormatter+Milliseconds.swift index 51aac81..73a922d 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/ISO8601DateFormatter+Milliseconds.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/ISO8601DateFormatter+Milliseconds.swift @@ -14,22 +14,31 @@ private struct GlobalDateFormatter: Sendable { static let cachedFormatter = Date.ISO8601FormatStyle(includingFractionalSeconds: true) static let parse: @Sendable (_ value: String) -> Date? = { + #if canImport(FoundationEssentials) + return { try? cachedFormatter.parse($0) } + #else if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) { return { try? cachedFormatter.parse($0) } } else { return { ISO8601DateFormatter.withMilliseconds.date(from: $0) } } + #endif }() static let format: @Sendable (_ value: Date) -> String = { + #if canImport(FoundationEssentials) + return { cachedFormatter.format($0) } + #else if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) { return { cachedFormatter.format($0) } } else { return { ISO8601DateFormatter.withMilliseconds.string(from: $0) } } + #endif }() } +#if !canImport(FoundationEssentials) private extension ISO8601DateFormatter { nonisolated(unsafe) static let withMilliseconds: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() @@ -44,6 +53,7 @@ private extension ISO8601DateFormatter { return formatter }() } +#endif // !canImport(FoundationEssentials) extension JSONDecoder.DateDecodingStrategy { static let iso8601WithMilliseconds: Self = custom { decoder in @@ -65,7 +75,7 @@ extension JSONEncoder.DateEncodingStrategy { } } -#if compiler(>=6) && compiler(<6.2) +#if compiler(<6.2) extension ISO8601DateFormatter: @unchecked @retroactive Sendable {} extension JSONDecoder.DateDecodingStrategy: @unchecked Sendable {} extension JSONEncoder.DateEncodingStrategy: @unchecked Sendable {} diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Snapshot/Snapshot.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Snapshot/Snapshot.swift index be5c02f..55f93c2 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Snapshot/Snapshot.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Snapshot/Snapshot.swift @@ -35,7 +35,7 @@ actor Snapshot { var cachedIteration: SnapshotIteration? /// A pointer to the last manifest updater, so updates can be serialized after the last request. - var lastUpdateManifestTask: Task? + var lastUpdateManifestTask: Task? /// The loaded datastores. var datastores: [DatastoreIdentifier: DiskPersistence.Datastore] = [:] @@ -179,7 +179,7 @@ extension Snapshot { /// - Returns: A ``/Swift/Task`` which contains the value of the updater upon completion. func updateManifest( updater: @Sendable @escaping (_ manifest: inout SnapshotManifest, _ iteration: inout SnapshotIteration) async throws -> T - ) -> Task where AccessMode == ReadWrite { + ) -> Task where AccessMode == ReadWrite { if let (manifest, iteration) = SnapshotTaskLocals.manifest(for: persistence) { return Task { var updatedManifest = manifest @@ -247,7 +247,7 @@ extension Snapshot { /// - Returns: A ``/Swift/Task`` which contains the value of the updater upon completion. func readManifest( accessor: @Sendable @escaping (_ manifest: SnapshotManifest, _ iteration: SnapshotIteration) async throws -> T - ) -> Task { + ) -> Task { if let (manifest, iteration) = SnapshotTaskLocals.manifest(for: persistence) { return Task { try await accessor(manifest, iteration) } diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/Transaction/Transaction.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/Transaction/Transaction.swift index 7555f8c..50320a6 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/Transaction/Transaction.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/Transaction/Transaction.swift @@ -17,7 +17,7 @@ extension DiskPersistence { unowned let parent: Transaction? weak var lastReadWriteChildTransaction: Transaction? - private(set) var task: Task! + private(set) var task: Task! let transactionIndex: Int let actionName: String? let options: UnsafeTransactionOptions @@ -54,7 +54,7 @@ extension DiskPersistence { private func attachTask( options: UnsafeTransactionOptions, @_inheritActorContext handler: @Sendable @escaping () async throws -> T - ) async -> Task { + ) async -> Task { let task = Task { isActive = true let returnValue = try await TransactionTaskLocals.with(transaction: self, for: persistence) { @@ -210,7 +210,7 @@ extension DiskPersistence { actionName: String?, options: UnsafeTransactionOptions, @_inheritActorContext handler: @Sendable @escaping (_ transaction: Transaction, _ isDurable: Bool) async throws -> T - ) async -> (Transaction, Task) { + ) async -> (Transaction, Task) { if let parent = Self.unsafeCurrentTransaction(for: persistence) { // print("[CDS] [\(persistence.storeURL.lastPathComponent)] Found parent \(parent.transactionIndex), making child \(transactionIndex)") let (child, task) = await parent.childTransaction( @@ -246,7 +246,7 @@ extension DiskPersistence { actionName: String?, options: UnsafeTransactionOptions, @_inheritActorContext handler: @Sendable @escaping (_ transaction: Transaction, _ isDurable: Bool) async throws -> T - ) async -> (Transaction, Task) { + ) async -> (Transaction, Task) { assert(!self.options.contains(.readOnly) || options.contains(.readOnly), "A child transaction was declared read-write, even though its parent was read-only!") let childTransaction = Transaction( persistence: persistence, @@ -1340,14 +1340,7 @@ extension DiskPersistence.Transaction { datastoreKey: DatastoreKey, bufferingPolicy limit: ObservationBufferingPolicy ) async throws -> AsyncCompactMapSequence>, ObservedEvent> { -#if swift(>=5.9) let (stream, observer) = AsyncStream.makeStream(of: ObservedEvent.self, bufferingPolicy: .init(limit)) -#else - var observer: DiskPersistence.EventObserver! - let stream = AsyncStream(ObservedEvent.self, bufferingPolicy: .init(limit)) { continuation in - observer = continuation - } -#endif let (datastore, _) = try await persistence.persistenceDatastore(for: datastoreKey) @@ -1383,14 +1376,14 @@ fileprivate protocol AnyDiskTransaction: Sendable {} fileprivate enum TransactionTaskLocals { @TaskLocal - static var transactionStorage: [ObjectIdentifier : AnyDiskTransaction] = [:] + static var transactionStorage: [ObjectIdentifier : any AnyDiskTransaction] = [:] - static func transaction(for persistence: DiskPersistence) -> AnyDiskTransaction? { + static func transaction(for persistence: DiskPersistence) -> (any AnyDiskTransaction)? { transactionStorage[ObjectIdentifier(persistence)] } static func with( - transaction: AnyDiskTransaction, + transaction: any AnyDiskTransaction, for persistence: DiskPersistence, operation: () async throws -> R ) async rethrows -> R { diff --git a/Sources/CodableDatastore/Persistence/Disk Persistence/TypedIdentifier.swift b/Sources/CodableDatastore/Persistence/Disk Persistence/TypedIdentifier.swift index 6306e89..832a2d1 100644 --- a/Sources/CodableDatastore/Persistence/Disk Persistence/TypedIdentifier.swift +++ b/Sources/CodableDatastore/Persistence/Disk Persistence/TypedIdentifier.swift @@ -23,12 +23,12 @@ protocol TypedIdentifierProtocol: RawRepresentable, Codable, Equatable, Hashable } extension TypedIdentifierProtocol { - init(from decoder: Decoder) throws { + init(from decoder: any Decoder) throws { let rawValue = try decoder.singleValueContainer().decode(String.self) self.init(rawValue: rawValue) } - func encode(to encoder: Encoder) throws { + func encode(to encoder: any Encoder) throws { var encoder = encoder.singleValueContainer() try encoder.encode(rawValue) } diff --git a/Sources/CodableDatastore/Persistence/Memory Persistence/MemoryPersistence.swift b/Sources/CodableDatastore/Persistence/Memory Persistence/MemoryPersistence.swift index d34727f..e1a94cc 100644 --- a/Sources/CodableDatastore/Persistence/Memory Persistence/MemoryPersistence.swift +++ b/Sources/CodableDatastore/Persistence/Memory Persistence/MemoryPersistence.swift @@ -17,7 +17,7 @@ extension MemoryPersistence { public func _withTransaction( actionName: String?, options: UnsafeTransactionOptions, - transaction: @Sendable (_ transaction: DatastoreInterfaceProtocol, _ isDurable: Bool) async throws -> T + transaction: @Sendable (_ transaction: any DatastoreInterfaceProtocol, _ isDurable: Bool) async throws -> T ) async throws -> T { preconditionFailure("Unimplemented") } diff --git a/Sources/CodableDatastore/Persistence/Persistence.swift b/Sources/CodableDatastore/Persistence/Persistence.swift index c9337ef..c80d41d 100644 --- a/Sources/CodableDatastore/Persistence/Persistence.swift +++ b/Sources/CodableDatastore/Persistence/Persistence.swift @@ -21,7 +21,7 @@ public protocol Persistence: Sendable { func _withTransaction( actionName: String?, options: UnsafeTransactionOptions, - @_inheritActorContext transaction: @Sendable (_ transaction: DatastoreInterfaceProtocol, _ isDurable: Bool) async throws -> T + @_inheritActorContext transaction: @Sendable (_ transaction: any DatastoreInterfaceProtocol, _ isDurable: Bool) async throws -> T ) async throws -> T } diff --git a/Tests/CodableDatastoreTests/DatastorePageEntryTests.swift b/Tests/CodableDatastoreTests/DatastorePageEntryTests.swift index 51efa26..ba4b780 100644 --- a/Tests/CodableDatastoreTests/DatastorePageEntryTests.swift +++ b/Tests/CodableDatastoreTests/DatastorePageEntryTests.swift @@ -120,7 +120,7 @@ final class DatastorePageEntryTests: XCTestCase { ) ) { error in switch error { - case BytesError.invalidMemorySize(targetSize: 1, targetType: _, actualSize: 0): break + case BytesError.BufferSizeError.invalidBufferSize(targetSize: 1, targetType: "Byte", actualSize: 0): break default: XCTFail("Unknown error \(error)") } @@ -135,7 +135,7 @@ final class DatastorePageEntryTests: XCTestCase { ) ) { error in switch error { - case BytesError.checkedSequenceNotFound: break + case BytesError.SequenceCheckError.checkedSequenceNotFound: break default: XCTFail("Unknown error \(error)") } @@ -152,7 +152,7 @@ final class DatastorePageEntryTests: XCTestCase { ) ) { error in switch error { - case BytesError.invalidMemorySize(targetSize: 1, targetType: _, actualSize: 0): break + case BytesError.BufferSizeError.invalidBufferSize(targetSize: 1, targetType: "Byte", actualSize: 0): break default: XCTFail("Unknown error \(error)") } @@ -181,7 +181,7 @@ final class DatastorePageEntryTests: XCTestCase { ) ) { error in switch error { - case BytesError.invalidMemorySize(targetSize: 1, targetType: _, actualSize: 0): break + case BytesError.BufferSizeError.invalidBufferSize(targetSize: 1, targetType: "Byte", actualSize: 0): break default: XCTFail("Unknown error \(error)") } diff --git a/Tests/CodableDatastoreTests/DiskPersistenceDatastoreIndexTests.swift b/Tests/CodableDatastoreTests/DiskPersistenceDatastoreIndexTests.swift index eca01d9..a129a34 100644 --- a/Tests/CodableDatastoreTests/DiskPersistenceDatastoreIndexTests.swift +++ b/Tests/CodableDatastoreTests/DiskPersistenceDatastoreIndexTests.swift @@ -11,6 +11,7 @@ @preconcurrency import Foundation #endif import XCTest +import Bytes @testable import CodableDatastore fileprivate struct SortError: Error, Equatable {}