From 887545d65c86b59a296add95c395a3f1e8f152b1 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Tue, 22 Apr 2025 15:47:58 -0700 Subject: [PATCH 1/6] Add swift-tagged trait --- Package.resolved | 11 +- Package.swift | 15 +- Package@swift-6.0.swift | 137 ++++++++++++++++++ .../StructuredQueriesCore/Traits/Tagged.swift | 31 ++++ 4 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 Package@swift-6.0.swift create mode 100644 Sources/StructuredQueriesCore/Traits/Tagged.swift diff --git a/Package.resolved b/Package.resolved index 272e6cc3..d32c11f9 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "c6d1e6c49f622359647bcd073a92db28e1dad8d549034617c7f827b702813be4", + "originHash" : "0609fed701a35e2ee3efbd46f681ff0361d000a8941571e234eab9bbe10f0643", "pins" : [ { "identity" : "combine-schedulers", @@ -91,6 +91,15 @@ "version" : "600.0.1" } }, + { + "identity" : "swift-tagged", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-tagged", + "state" : { + "revision" : "3907a9438f5b57d317001dc99f3f11b46882272b", + "version" : "0.10.0" + } + }, { "identity" : "xctest-dynamic-overlay", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 60bd4306..95069d75 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.0 +// swift-tools-version: 6.1 import CompilerPluginSupport import PackageDescription @@ -29,11 +29,19 @@ let package = Package( targets: ["StructuredQueriesSQLite"] ), ], + traits: [ + .trait( + name: "TaggedStructuredQueries", + description: "Introduce StructuredQueries conformances to the swift-tagged package.", + enabledTraits: [] + ), + ], dependencies: [ .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.8.1"), .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.0"), .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.1"), + .package(url: "https://github.com/pointfreeco/swift-tagged", from: "0.10.0"), .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.5.2"), .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"601.0.0"), ], @@ -43,6 +51,11 @@ let package = Package( dependencies: [ "StructuredQueriesSupport", .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), + .product( + name: "Tagged", + package: "swift-tagged", + condition: .when(traits: ["TaggedStructuredQueries"]) + ), ] ), .target( diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift new file mode 100644 index 00000000..4ad6c7f3 --- /dev/null +++ b/Package@swift-6.0.swift @@ -0,0 +1,137 @@ +// swift-tools-version: 6.0 + +import CompilerPluginSupport +import PackageDescription + +let package = Package( + name: "swift-structured-queries", + platforms: [ + .iOS(.v13), + .macOS(.v10_15), + .tvOS(.v13), + .watchOS(.v6), + ], + products: [ + .library( + name: "StructuredQueries", + targets: ["StructuredQueries"] + ), + .library( + name: "StructuredQueriesCore", + targets: ["StructuredQueriesCore"] + ), + .library( + name: "StructuredQueriesTestSupport", + targets: ["StructuredQueriesTestSupport"] + ), + .library( + name: "_StructuredQueriesSQLite", + targets: ["StructuredQueriesSQLite"] + ), + ], + dependencies: [ + .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), + .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.8.1"), + .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.0"), + .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.1"), + .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.5.2"), + .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"601.0.0"), + ], + targets: [ + .target( + name: "StructuredQueriesCore", + dependencies: [ + "StructuredQueriesSupport", + .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), + ] + ), + .target( + name: "StructuredQueries", + dependencies: [ + "StructuredQueriesCore", + "StructuredQueriesMacros", + ] + ), + .macro( + name: "StructuredQueriesMacros", + dependencies: [ + "StructuredQueriesSupport", + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + ] + ), + .target( + name: "StructuredQueriesSQLite", + dependencies: [ + "StructuredQueries" + ] + ), + .target( + name: "StructuredQueriesTestSupport", + dependencies: [ + "StructuredQueriesCore", + .product(name: "CustomDump", package: "swift-custom-dump"), + .product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"), + ] + ), + .testTarget( + name: "StructuredQueriesMacrosTests", + dependencies: [ + "StructuredQueries", + "StructuredQueriesMacros", + .product(name: "IssueReporting", package: "xctest-dynamic-overlay"), + .product(name: "MacroTesting", package: "swift-macro-testing"), + ] + ), + .testTarget( + name: "StructuredQueriesTests", + dependencies: [ + "StructuredQueries", + "StructuredQueriesSQLite", + "StructuredQueriesTestSupport", + .product(name: "CustomDump", package: "swift-custom-dump"), + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"), + ] + ), + + .target(name: "StructuredQueriesSupport"), + ], + swiftLanguageModes: [.v6] +) + +let swiftSettings: [SwiftSetting] = [ + .enableUpcomingFeature("MemberImportVisibility") + // .unsafeFlags([ + // "-Xfrontend", + // "-warn-long-function-bodies=50", + // "-Xfrontend", + // "-warn-long-expression-type-checking=50", + // ]) +] + +for index in package.targets.indices { + package.targets[index].swiftSettings = swiftSettings +} + +#if !os(Darwin) + package.targets.append( + .systemLibrary( + name: "StructuredQueriesSQLite3", + providers: [.apt(["libsqlite3-dev"])] + ) + ) + + for index in package.targets.indices { + if package.targets[index].name == "StructuredQueriesSQLite" { + package.targets[index].dependencies.append("StructuredQueriesSQLite3") + } + } +#endif + +#if !os(Windows) + // Add the documentation compiler plugin if possible + package.dependencies.append( + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0") + ) +#endif diff --git a/Sources/StructuredQueriesCore/Traits/Tagged.swift b/Sources/StructuredQueriesCore/Traits/Tagged.swift new file mode 100644 index 00000000..4788f936 --- /dev/null +++ b/Sources/StructuredQueriesCore/Traits/Tagged.swift @@ -0,0 +1,31 @@ +#if TaggedStructuredQueries + import Tagged + + extension Tagged: _OptionalPromotable where RawValue: _OptionalPromotable {} + + extension Tagged: QueryBindable where RawValue: QueryBindable {} + + extension Tagged: QueryDecodable where RawValue: QueryDecodable {} + + extension Tagged: QueryExpression where RawValue: QueryExpression { + public var queryFragment: QueryFragment { + rawValue.queryFragment + } + } + + extension Tagged: QueryRepresentable where RawValue: QueryRepresentable { + public init(queryOutput: RawValue.QueryOutput) { + self.init(RawValue(queryOutput: queryOutput)) + } + + public var queryOutput: RawValue.QueryOutput { + rawValue.queryOutput + } + } + + extension Tagged: SQLiteType where RawValue: SQLiteType { + public static var typeAffinity: SQLiteTypeAffinity { + RawValue.typeAffinity + } + } +#endif From d3352fd59a36fbf0702386dd1916968b55a28344 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Tue, 22 Apr 2025 16:28:00 -0700 Subject: [PATCH 2/6] Add TaggedStructuredQueries trait --- .../Articles/DefiningYourSchema.md | 45 +++++++++++++++++++ .../StructuredQueriesCore/Traits/Tagged.swift | 10 +++-- .../StructuredQueriesTests/UpdateTests.swift | 4 +- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md b/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md index 05b19f7f..14bc2495 100644 --- a/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md +++ b/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md @@ -331,6 +331,51 @@ With that you can insert reminders with notes like so: } } +#### Tagged identifiers + +The [Tagged](https://github.com/pointfreeco/swift-tagged) library provides lightweight syntax for +introducing type-safe identifiers (and more) to your models. StructuredQueries ships support for +Tagged with a `TaggedStructuredQueries` package trait, which is available starting from Swift 6.1. + +To enable the trait, specify it in the Package.swift file that depends on StructuredQueries: + +```diff + .package( + url: "https://github.com/pointfreeco/swift-structured-queries", + from: "0.2.0", ++ traits: ["TaggedStructuredQueries"] + ), +``` + +This will allow you to introduce distinct `Tagged` identifiers throughout your schema: + +```diff + @Table + struct RemindersList: Identifiable { +- let id: Int ++ typealias ID = Tagged ++ let id: ID + // ... + } + @Table + struct Reminder: Identifiable { +- let id: Int ++ typealias ID = Tagged ++ let id: ID + // ... + var remindersList: Reminder.ID + } +``` + +This adds a new layer of type-safety when constructing queries. Previously comparing a +`RemindersList.ID` to a `Reminder.ID` would compile just fine, even though it is a nonsensical thing +to do. But now, such a comparison is a compile time error: + +``` +RemindersList.leftJoin(Reminder.all) { + $0.id == $1.id // 🛑 Requires the types 'Reminder.ID' and 'RemindersList.ID' be equivalent +} + ### Primary keyed tables It is possible to tell let the `@Table` macro know which property of your data type is the primary diff --git a/Sources/StructuredQueriesCore/Traits/Tagged.swift b/Sources/StructuredQueriesCore/Traits/Tagged.swift index 4788f936..538ec4e1 100644 --- a/Sources/StructuredQueriesCore/Traits/Tagged.swift +++ b/Sources/StructuredQueriesCore/Traits/Tagged.swift @@ -14,12 +14,14 @@ } extension Tagged: QueryRepresentable where RawValue: QueryRepresentable { - public init(queryOutput: RawValue.QueryOutput) { - self.init(RawValue(queryOutput: queryOutput)) + public typealias QueryOutput = Tagged + + public var queryOutput: QueryOutput { + QueryOutput(rawValue: self.rawValue.queryOutput) } - public var queryOutput: RawValue.QueryOutput { - rawValue.queryOutput + public init(queryOutput: QueryOutput) { + self.init(rawValue: RawValue(queryOutput: queryOutput.rawValue)) } } diff --git a/Tests/StructuredQueriesTests/UpdateTests.swift b/Tests/StructuredQueriesTests/UpdateTests.swift index f7c8df05..91a99d92 100644 --- a/Tests/StructuredQueriesTests/UpdateTests.swift +++ b/Tests/StructuredQueriesTests/UpdateTests.swift @@ -365,7 +365,7 @@ extension SnapshotTests { WHERE ("reminders"."id" = 1) RETURNING "dueDate" """ - }results: { + } results: { """ ┌─────┐ │ nil │ @@ -383,7 +383,7 @@ extension SnapshotTests { WHERE ("reminders"."id" = 1) RETURNING "dueDate" """ - }results: { + } results: { """ ┌────────────────────────────────┐ │ Date(2018-01-29T00:08:00.000Z) │ From 3a9bfba49801da4b8ca0b39cf7efc1e2ebdf727d Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Tue, 22 Apr 2025 16:36:36 -0700 Subject: [PATCH 3/6] Use same convention as Subprocess --- Package.swift | 6 +++--- .../Documentation.docc/Articles/DefiningYourSchema.md | 4 ++-- Sources/StructuredQueriesCore/Traits/Tagged.swift | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Package.swift b/Package.swift index 95069d75..a000c27f 100644 --- a/Package.swift +++ b/Package.swift @@ -31,10 +31,10 @@ let package = Package( ], traits: [ .trait( - name: "TaggedStructuredQueries", + name: "StructuredQueriesTagged", description: "Introduce StructuredQueries conformances to the swift-tagged package.", enabledTraits: [] - ), + ) ], dependencies: [ .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), @@ -54,7 +54,7 @@ let package = Package( .product( name: "Tagged", package: "swift-tagged", - condition: .when(traits: ["TaggedStructuredQueries"]) + condition: .when(traits: ["StructuredQueriesTagged"]) ), ] ), diff --git a/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md b/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md index 14bc2495..61034717 100644 --- a/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md +++ b/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md @@ -335,7 +335,7 @@ With that you can insert reminders with notes like so: The [Tagged](https://github.com/pointfreeco/swift-tagged) library provides lightweight syntax for introducing type-safe identifiers (and more) to your models. StructuredQueries ships support for -Tagged with a `TaggedStructuredQueries` package trait, which is available starting from Swift 6.1. +Tagged with a `StructuredQueriesTagged` package trait, which is available starting from Swift 6.1. To enable the trait, specify it in the Package.swift file that depends on StructuredQueries: @@ -343,7 +343,7 @@ To enable the trait, specify it in the Package.swift file that depends on Struct .package( url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.2.0", -+ traits: ["TaggedStructuredQueries"] ++ traits: ["StructuredQueriesTagged"] ), ``` diff --git a/Sources/StructuredQueriesCore/Traits/Tagged.swift b/Sources/StructuredQueriesCore/Traits/Tagged.swift index 538ec4e1..dfc8b4d2 100644 --- a/Sources/StructuredQueriesCore/Traits/Tagged.swift +++ b/Sources/StructuredQueriesCore/Traits/Tagged.swift @@ -1,4 +1,4 @@ -#if TaggedStructuredQueries +#if StructuredQueriesTagged import Tagged extension Tagged: _OptionalPromotable where RawValue: _OptionalPromotable {} From 3a5a0f0c7111500f43800787f4edcbf13c6b3e9f Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Tue, 22 Apr 2025 16:55:59 -0700 Subject: [PATCH 4/6] wip --- README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 840582c2..f9d80816 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,10 @@ it's as simple as adding it to your `Package.swift`: ``` swift dependencies: [ - .package(url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.1.0"), + .package( + url: "https://github.com/pointfreeco/swift-structured-queries", + from: "0.1.0" + ), ] ``` @@ -231,6 +234,23 @@ And then adding the product to any target that needs access to the library: .product(name: "StructuredQueries", package: "swift-structured-queries"), ``` +If you are on Swift 6.1 or greater, you can enable package traits that extend the library with +support for other packages. For example, you can introduce type-safe identifiers to your tables +_via_ [Tagged](https://github.com/pointfreeco/swift-tagged) by enabling the +`StructuredQueriesTagged` trait: + +```diff + dependencies: [ + .package( + url: "https://github.com/pointfreeco/swift-structured-queries", + from: "0.1.0", ++ traits: [ ++ "StructuredQueriesTagged", ++ ] + ), + ] +``` + ## Community If you want to discuss this library or have a question about how to use it to solve a particular From af47b2585385dead3cd5c4d08c7fed77117af18a Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Thu, 29 May 2025 10:40:24 -0700 Subject: [PATCH 5/6] wip --- Package.resolved | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Package.resolved b/Package.resolved index 868533fc..f59c9a7b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "ec2862d1364536fc22ec56a3094e7a034bbc7da8", - "version" : "1.8.1" + "revision" : "4c90d6b2b9bf0911af87b103bb40f41771891596", + "version" : "1.9.2" } }, { @@ -69,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-macro-testing", "state" : { - "revision" : "0b80a098d4805a21c412b65f01ffde7b01aab2fa", - "version" : "0.6.0" + "revision" : "2de00af725ff4c43c9a90d7893835de312653169", + "version" : "0.6.3" } }, { @@ -78,8 +78,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "b2d4cb30735f4fbc3a01963a9c658336dd21e9ba", - "version" : "1.18.1" + "revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb", + "version" : "1.18.4" } }, { @@ -87,8 +87,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "0687f71944021d616d34d922343dcef086855920", - "version" : "600.0.1" + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" } }, { From 1e14a9132b3f2f98e4c63844a74feb67eeaea791 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Thu, 29 May 2025 10:41:27 -0700 Subject: [PATCH 6/6] wip --- Package.swift | 4 ++-- Package@swift-6.0.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Package.swift b/Package.swift index e2c78e24..5009ce65 100644 --- a/Package.swift +++ b/Package.swift @@ -39,8 +39,8 @@ let package = Package( dependencies: [ .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.8.1"), - .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.0"), - .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.1"), + .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.3"), + .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.4"), .package(url: "https://github.com/pointfreeco/swift-tagged", from: "0.10.0"), .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.5.2"), .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"602.0.0"), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 4ad6c7f3..94af9029 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -32,10 +32,10 @@ let package = Package( dependencies: [ .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.3"), .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.8.1"), - .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.0"), - .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.1"), + .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.3"), + .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.4"), .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.5.2"), - .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"601.0.0"), + .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"602.0.0"), ], targets: [ .target(