diff --git a/Package.resolved b/Package.resolved index fc501a3..868533f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "587a4f16d6abcf8ce2a9823d3a1470bab537338281a888ef1c84c91c18be68a5", + "originHash" : "a9cc8edf77ac5412b54a8aed27b41c2fc10588741da62b1886a48257aa83a50f", "pins" : [ { "identity" : "combine-schedulers", @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-dependencies", "state" : { - "revision" : "4c90d6b2b9bf0911af87b103bb40f41771891596", - "version" : "1.9.2" + "revision" : "ec2862d1364536fc22ec56a3094e7a034bbc7da8", + "version" : "1.8.1" } }, { @@ -69,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-macro-testing", "state" : { - "revision" : "cfe474c7e97d429ea31eefed2e9ab8c7c74260f9", - "version" : "0.6.2" + "revision" : "0b80a098d4805a21c412b65f01ffde7b01aab2fa", + "version" : "0.6.0" } }, { @@ -78,8 +78,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb", - "version" : "1.18.4" + "revision" : "b2d4cb30735f4fbc3a01963a9c658336dd21e9ba", + "version" : "1.18.1" } }, { @@ -87,8 +87,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", - "version" : "601.0.1" + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" + } + }, + { + "identity" : "swift-tagged", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-tagged", + "state" : { + "revision" : "3907a9438f5b57d317001dc99f3f11b46882272b", + "version" : "0.10.0" } }, { diff --git a/Package.swift b/Package.swift index da44af9..e2c78e2 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: "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"), .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"..<"602.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: ["StructuredQueriesTagged"]) + ), ] ), .target( diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift new file mode 100644 index 0000000..4ad6c7f --- /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/README.md b/README.md index 1524911..eb5cd02 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,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" + ), ] ``` @@ -230,6 +233,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 diff --git a/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md b/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md index 7e57afa..8b7d2d4 100644 --- a/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md +++ b/Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md @@ -257,6 +257,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 `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: + +```diff + .package( + url: "https://github.com/pointfreeco/swift-structured-queries", + from: "0.2.0", ++ traits: ["StructuredQueriesTagged"] + ), +``` + +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 +} + #### Default representations for dates and UUIDs While some relational databases, like MySQL and Postgres, have native types for dates and UUIDs, diff --git a/Sources/StructuredQueriesCore/Traits/Tagged.swift b/Sources/StructuredQueriesCore/Traits/Tagged.swift new file mode 100644 index 0000000..dfc8b4d --- /dev/null +++ b/Sources/StructuredQueriesCore/Traits/Tagged.swift @@ -0,0 +1,33 @@ +#if StructuredQueriesTagged + 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 typealias QueryOutput = Tagged + + public var queryOutput: QueryOutput { + QueryOutput(rawValue: self.rawValue.queryOutput) + } + + public init(queryOutput: QueryOutput) { + self.init(rawValue: RawValue(queryOutput: queryOutput.rawValue)) + } + } + + extension Tagged: SQLiteType where RawValue: SQLiteType { + public static var typeAffinity: SQLiteTypeAffinity { + RawValue.typeAffinity + } + } +#endif