Skip to content
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

Non-Escaping Transactions and Sendable Conformance #206

Merged
merged 5 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 23 additions & 0 deletions .github/workflows/api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: API Check

on:
pull_request:
branches:
- main

jobs:
API-Check:
name: Diagnose API Breaking Changes
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Source
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Mark Workspace As Safe
# https://github.com/actions/checkout/issues/766
run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
- name: Diagnose API Breaking Changes
run: |
swift package diagnose-api-breaking-changes origin/main --products CodableDatastore
10 changes: 8 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/mochidev/AsyncSequenceReader.git", .upToNextMinor(from: "0.1.2")),
.package(url: "https://github.com/mochidev/AsyncSequenceReader.git", .upToNextMinor(from: "0.2.1")),
.package(url: "https://github.com/mochidev/Bytes.git", .upToNextMinor(from: "0.3.0")),
],
targets: [
Expand All @@ -27,11 +27,17 @@ let package = Package(
dependencies: [
"AsyncSequenceReader",
"Bytes"
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]
),
.testTarget(
name: "CodableDatastoreTests",
dependencies: ["CodableDatastore"]
dependencies: ["CodableDatastore"],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]
),
]
)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Please check the [releases](https://github.com/mochidev/CodableDatastore/release
dependencies: [
.package(
url: "https://github.com/mochidev/CodableDatastore.git",
.upToNextMinor(from: "0.2.5")
.upToNextMinor(from: "0.3.0")
),
],
...
Expand Down
4 changes: 2 additions & 2 deletions Sources/CodableDatastore/Datastore/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-05-10.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

public struct Configuration {
public struct Configuration: Sendable {
/// The size of a single page of data on disk and in memory.
///
/// Applications that deal with large objects may want to consider increasing this appropriately,
Expand Down
160 changes: 88 additions & 72 deletions Sources/CodableDatastore/Datastore/Datastore.swift

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Sources/CodableDatastore/Datastore/DatastoreDescriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-06-11.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

import Foundation

/// A description of a ``Datastore``'s requirements of a persistence.
///
/// A persistence is expected to save a description and retrieve it when a connected ``Datastore`` requests it. The ``Datastore`` then uses it to compute if indexes need to be invalidated or re-built.
public struct DatastoreDescriptor: Equatable, Hashable {
public struct DatastoreDescriptor: Equatable, Hashable, Sendable {
/// The version that was current at time of serialization.
///
/// If a ``Datastore`` cannot decode this version, the datastore is presumed inaccessible, and any reads or writes will fail.
Expand Down Expand Up @@ -96,7 +96,7 @@ extension DatastoreDescriptor {
/// A description of an Index used by a ``Datastore``.
///
/// This information is used to determine which indexes must be invalidated or re-built, and which can be used as is. Additionally, it informs which properties must be reported along with any writes to keep existing indexes up to date.
public struct IndexDescriptor: Codable, Equatable, Hashable, Comparable {
public struct IndexDescriptor: Codable, Equatable, Hashable, Comparable, Sendable {
/// The version that was first used to persist an index to disk.
///
/// This is used to determine if an index must be re-built purely because something about how the index changed in a way that could not be automatically determined, such as Codable conformance changing.
Expand Down
2 changes: 1 addition & 1 deletion Sources/CodableDatastore/Datastore/DatastoreError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-06-18.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

import Foundation
Expand Down
8 changes: 4 additions & 4 deletions Sources/CodableDatastore/Datastore/DatastoreFormat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,21 @@ import Foundation
/// - Note: If your ``Instance`` type is ``/Swift/Identifiable``, you should _not_ declare an index for `id` — special accessors are created on your behalf that can be used instead.
///
/// - Important: We discourage declaring non-static stored and computed properties on your conforming type, as that will polute the key-path namespace of the format which is used for generating getters on the datastore.
public protocol DatastoreFormat<Version, Instance, Identifier> {
public protocol DatastoreFormat<Version, Instance, Identifier>: Sendable {
/// A type representing the version of the datastore on disk.
///
/// Best represented as an enum, this represents the every single version of the datastore you wish to be able to decode from disk. Assign a new version any time the codable representation or the representation of indexes is no longer backwards compatible.
///
/// The various ``Datastore`` initializers take a disctionary that maps between these versions and the most up-to-date Instance type, and will provide an opportunity to use legacy representations to decode the data to the expected type.
associatedtype Version: RawRepresentable & Hashable & CaseIterable where Version.RawValue: Indexable & Comparable
associatedtype Version: RawRepresentable & Hashable & CaseIterable & Sendable where Version.RawValue: Indexable & Comparable

/// The most up-to-date representation you use in your codebase.
associatedtype Instance: Codable
associatedtype Instance: Codable & Sendable

/// The identifier to be used when de-duplicating instances saved in the persistence.
///
/// Although ``Instance`` does _not_ need to be ``Identifiable``, a consistent identifier must still be provided for every instance to retrive and persist them. This identifier can be different from `Instance.ID` if truly necessary, though most conformers can simply set it to `Instance.ID`
associatedtype Identifier: Indexable & DiscreteIndexable
associatedtype Identifier: Indexable & DiscreteIndexable & Sendable

/// A default initializer creating a format instance the datastore can use for evaluation.
init()
Expand Down
4 changes: 2 additions & 2 deletions Sources/CodableDatastore/Datastore/DatastoreKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-07-01.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

public struct DatastoreKey: RawRepresentable, Hashable, Comparable {
public struct DatastoreKey: RawRepresentable, Hashable, Comparable, Sendable {
public var rawValue: String

public init(rawValue: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-07-20.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

import Foundation
Expand Down
5 changes: 3 additions & 2 deletions Sources/CodableDatastore/Datastore/ObservedEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-07-12.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

import Foundation
Expand Down Expand Up @@ -44,8 +44,9 @@ public enum ObservedEvent<IdentifierType, Entry> {
}

extension ObservedEvent: Identifiable where IdentifierType: Hashable {}
extension ObservedEvent: Sendable where IdentifierType: Sendable, Entry: Sendable {}

public struct ObservationEntry {
public struct ObservationEntry: Sendable {
var versionData: Data
var instanceData: Data
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/CodableDatastore/Datastore/Progress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-06-15.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

import Foundation

public typealias ProgressHandler = (_ progress: Progress) -> Void
public typealias ProgressHandler = @Sendable (_ progress: Progress) -> Void

public enum Progress {
public enum Progress: Sendable {
case evaluating
case working(current: Int, total: Int)
case complete(total: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-06-15.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

import Foundation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-07-12.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

public protocol TypedAsyncSequence<Element>: AsyncSequence {}
Expand Down
4 changes: 2 additions & 2 deletions Sources/CodableDatastore/Debug/GlobalTimer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-07-05.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

import Foundation

actor GlobalTimer {
var totalTime: TimeInterval = 0
var totalSamples: Int = 0
static var global = GlobalTimer()
static let global = GlobalTimer()

func submit(time: TimeInterval, sampleRate: Int = 100) {
precondition(sampleRate > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//

/// A helper type for passing around metadata about an index.
public struct GeneratedIndexRepresentation<Instance> {
public struct GeneratedIndexRepresentation<Instance: Sendable>: Sendable {
/// The name the index should be serialized under.
public var indexName: IndexName

Expand Down
4 changes: 2 additions & 2 deletions Sources/CodableDatastore/Indexes/IndexName.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-07-20.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

/// A typed name that an index is keyed under. This is typically the path component of the key path that leads to an index.
public struct IndexName: RawRepresentable, Hashable, Comparable {
public struct IndexName: RawRepresentable, Hashable, Comparable, Sendable {
public var rawValue: String

public init(rawValue: String) {
Expand Down
14 changes: 8 additions & 6 deletions Sources/CodableDatastore/Indexes/IndexRangeExpression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// CodableDatastore
//
// Created by Dimitri Bouniol on 2023-06-05.
// Copyright © 2023 Mochi Development, Inc. All rights reserved.
// Copyright © 2023-24 Mochi Development, Inc. All rights reserved.
//

/// A type of bound found on either end of a range.
Expand All @@ -21,7 +21,7 @@ public enum RangeBoundExpression<Bound: Comparable>: Equatable {
extension RangeBoundExpression: Sendable where Bound: Sendable { }

/// The order a range is declared in.
public enum RangeOrder: Equatable {
public enum RangeOrder: Equatable, Sendable {
/// The range is in ascending order.
case ascending

Expand Down Expand Up @@ -60,7 +60,7 @@ extension IndexRangeExpression {
)
}

func applying(_ newOrder: RangeOrder) -> some IndexRangeExpression<Bound> {
func applying(_ newOrder: RangeOrder) -> IndexRange<Bound> {
IndexRange(
lower: lowerBoundExpression,
upper: upperBoundExpression,
Expand All @@ -74,7 +74,7 @@ extension IndexRangeExpression {
}

/// The position relative to a range.
public enum RangePosition: Equatable {
public enum RangePosition: Equatable, Sendable {
/// A value appears before the range.
case before

Expand Down Expand Up @@ -189,6 +189,8 @@ public struct IndexRange<Bound: Comparable>: IndexRangeExpression {
}
}

extension IndexRange: Sendable where Bound: Sendable {}

extension IndexRange where Bound == Never {
static let unbounded = IndexRange()
}
Expand All @@ -199,7 +201,7 @@ postfix operator ..>
extension Comparable {
/// A range excluding the lower bound.
@inlinable
public static func ..> (minimum: Self, maximum: Self) -> some IndexRangeExpression<Self> {
public static func ..> (minimum: Self, maximum: Self) -> IndexRange<Self> {
precondition(minimum == minimum, "Range cannot have an unordered lower bound.")
precondition(maximum == maximum, "Range cannot have an unordered upper bound.")
precondition(minimum <= maximum, "Range lower bound must be less than upper bound.")
Expand All @@ -211,7 +213,7 @@ extension Comparable {

/// A partial range excluding the lower bound.
@inlinable
public static postfix func ..> (minimum: Self) -> some IndexRangeExpression<Self> {
public static postfix func ..> (minimum: Self) -> IndexRange<Self> {
precondition(minimum == minimum, "Range cannot have an unordered lower bound.")
return IndexRange(
lower: .excluding(minimum),
Expand Down
Loading
Loading