diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..d464453 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,33 @@ +{ + "originHash" : "d21c129c4ed8fa4634fe7ff3550a3ae14e9eda6d1d7d01017d1e369a1fc23826", + "pins" : [ + { + "identity" : "grdb.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/groue/GRDB.swift.git", + "state" : { + "branch" : "GRDB7", + "revision" : "3ecb5c54553559217592d21a6d9841becb891b38" + } + }, + { + "identity" : "gtfs", + "kind" : "remoteSourceControl", + "location" : "https://github.com/emma-k-alexandra/GTFS.git", + "state" : { + "revision" : "92197f4c41aba53889a2db1e2e9d6dbe4022a223", + "version" : "1.0.1" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "ebc7251dd5b37f627c93698e4374084d98409633", + "version" : "1.28.2" + } + } + ], + "version" : 3 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..d89e395 --- /dev/null +++ b/Package.swift @@ -0,0 +1,36 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "gtfs-db-swift", + defaultLocalization: "en", // for tests + platforms: [ + .iOS(.v13), + .macOS(.v10_15), + .tvOS(.v13), + .watchOS(.v7), + ], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "GtfsDb", + targets: ["GtfsDb"]), + ], + dependencies: [ + .package(url: "https://github.com/groue/GRDB.swift.git", branch: "GRDB7"), + .package(url: "https://github.com/emma-k-alexandra/GTFS.git", .upToNextMajor(from: "1.0.1")) + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "GtfsDb", + dependencies: [.product(name: "GRDB", package: "grdb.swift"), "GTFS"]), + .testTarget( + name: "GtfsDbTests", + dependencies: ["GtfsDb"] + ), + ] +) diff --git a/Sources/GtfsDb/GtfsDb.swift b/Sources/GtfsDb/GtfsDb.swift new file mode 100644 index 0000000..08b22b8 --- /dev/null +++ b/Sources/GtfsDb/GtfsDb.swift @@ -0,0 +1,2 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book diff --git a/Sources/GtfsDb/Tables/Agency.swift b/Sources/GtfsDb/Tables/Agency.swift new file mode 100644 index 0000000..493ed3f --- /dev/null +++ b/Sources/GtfsDb/Tables/Agency.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Agency: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "agency" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/Attribution.swift b/Sources/GtfsDb/Tables/Attribution.swift new file mode 100644 index 0000000..0cdf72a --- /dev/null +++ b/Sources/GtfsDb/Tables/Attribution.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Attribution: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "attributions" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/CalendarDate.swift b/Sources/GtfsDb/Tables/CalendarDate.swift new file mode 100644 index 0000000..d1d4362 --- /dev/null +++ b/Sources/GtfsDb/Tables/CalendarDate.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension CalendarDate: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "calendar_dates" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/FareRule.swift b/Sources/GtfsDb/Tables/FareRule.swift new file mode 100644 index 0000000..552e55b --- /dev/null +++ b/Sources/GtfsDb/Tables/FareRule.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension FareRule: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "fare_rules" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/FeedInfo.swift b/Sources/GtfsDb/Tables/FeedInfo.swift new file mode 100644 index 0000000..fc6307e --- /dev/null +++ b/Sources/GtfsDb/Tables/FeedInfo.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension FeedInfo: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "feed_info" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/Frequency.swift b/Sources/GtfsDb/Tables/Frequency.swift new file mode 100644 index 0000000..88b377c --- /dev/null +++ b/Sources/GtfsDb/Tables/Frequency.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Frequency: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "frequencies" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/GTFSCalendar.swift b/Sources/GtfsDb/Tables/GTFSCalendar.swift new file mode 100644 index 0000000..a5c4419 --- /dev/null +++ b/Sources/GtfsDb/Tables/GTFSCalendar.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension GTFSCalendar: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "calendar" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/Pathway.swift b/Sources/GtfsDb/Tables/Pathway.swift new file mode 100644 index 0000000..2920ee4 --- /dev/null +++ b/Sources/GtfsDb/Tables/Pathway.swift @@ -0,0 +1,9 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Pathway: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "pathways" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} + diff --git a/Sources/GtfsDb/Tables/Route.swift b/Sources/GtfsDb/Tables/Route.swift new file mode 100644 index 0000000..8ba072d --- /dev/null +++ b/Sources/GtfsDb/Tables/Route.swift @@ -0,0 +1,9 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Route: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "routes" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} + diff --git a/Sources/GtfsDb/Tables/Shape.swift b/Sources/GtfsDb/Tables/Shape.swift new file mode 100644 index 0000000..702cbd9 --- /dev/null +++ b/Sources/GtfsDb/Tables/Shape.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Shape: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "shapes" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/Stop.swift b/Sources/GtfsDb/Tables/Stop.swift new file mode 100644 index 0000000..c4b2e03 --- /dev/null +++ b/Sources/GtfsDb/Tables/Stop.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Stop: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "stops" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/StopTime.swift b/Sources/GtfsDb/Tables/StopTime.swift new file mode 100644 index 0000000..d99e5e1 --- /dev/null +++ b/Sources/GtfsDb/Tables/StopTime.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension StopTime: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "stop_times" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/Transfer.swift b/Sources/GtfsDb/Tables/Transfer.swift new file mode 100644 index 0000000..ae81de1 --- /dev/null +++ b/Sources/GtfsDb/Tables/Transfer.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Transfer: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "transfers" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/Translation.swift b/Sources/GtfsDb/Tables/Translation.swift new file mode 100644 index 0000000..65d2b8b --- /dev/null +++ b/Sources/GtfsDb/Tables/Translation.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Translation: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "translations" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Sources/GtfsDb/Tables/Trip.swift b/Sources/GtfsDb/Tables/Trip.swift new file mode 100644 index 0000000..085bf51 --- /dev/null +++ b/Sources/GtfsDb/Tables/Trip.swift @@ -0,0 +1,8 @@ +import Foundation +import GRDB +@preconcurrency import GTFS + +extension Trip: @retroactive @unchecked Sendable, @retroactive FetchableRecord, @retroactive TableRecord { + public static var databaseTableName: String { "trips" } + public static let databaseColumnDecodingStrategy = DatabaseColumnDecodingStrategy.convertFromSnakeCase +} diff --git a/Tests/GtfsDbTests/GtfsDbTests.swift b/Tests/GtfsDbTests/GtfsDbTests.swift new file mode 100644 index 0000000..80f2185 --- /dev/null +++ b/Tests/GtfsDbTests/GtfsDbTests.swift @@ -0,0 +1,92 @@ +import Testing +import GRDB +import GTFS +@testable import GtfsDb + +@Suite("GTFS table count tests") +struct GtfsDbTests { + static let dbPath = "/Users/ffeli/Source/PhilipSoftware/BusCyprus/NPT_9_google_transit.sqlite" + var dbQueue: DatabaseQueue + + init() throws { + var config = Configuration() + config.readonly = true + dbQueue = try DatabaseQueue(path: GtfsDbTests.dbPath, configuration: config) + } + + @Test func read_agencies() async throws { + let count = try await dbQueue.read { db in try Agency.fetchCount(db) } + print("agencies", count) + } + + @Test func read_routes() async throws { + let count = try await dbQueue.read { db in try Route.fetchCount(db) } + print("routes", count) + } + + @Test func read_stops() async throws { + let count = try await dbQueue.read { db in try Stop.fetchCount(db) } + print("stops", count) + } + + @Test func read_trips() async throws { + let count = try await dbQueue.read { db in try Trip.fetchCount(db) } + print("trips", count) + } + + @Test func read_stop_times() async throws { + let count = try await dbQueue.read { db in try StopTime.fetchCount(db) } + print("stop_times", count) + } + + @Test func read_calendar() async throws { + let count = try await dbQueue.read { db in try GTFSCalendar.fetchCount(db) } + print("calendar", count) + } + + @Test func read_calendar_dates() async throws { + let count = try await dbQueue.read { db in try CalendarDate.fetchCount(db) } + print("calendar_dates", count) + } + + @Test func read_fare_rules() async throws { + let count = try await dbQueue.read { db in try FareRule.fetchCount(db) } + print("fare_rules", count) + } + + @Test func read_shapes() async throws { + let count = try await dbQueue.read { db in try Shape.fetchCount(db) } + print("shapes", count) + } + + @Test func read_frequencies() async throws { + let count = try await dbQueue.read { db in try Frequency.fetchCount(db) } + print("frequencies", count) + } + + @Test func read_transfers() async throws { + let count = try await dbQueue.read { db in try Transfer.fetchCount(db) } + print("transfers", count) + } + + @Test func read_pathways() async throws { + let count = try await dbQueue.read { db in try Pathway.fetchCount(db) } + print("pathways", count) + } + + @Test func read_feed_info() async throws { + let count = try await dbQueue.read { db in try FeedInfo.fetchCount(db) } + print("feed_info", count) + } + + @Test func read_translations() async throws { + let count = try await dbQueue.read { db in try Translation.fetchCount(db) } + print("translations", count) + } + + @Test func read_attributions() async throws { + let count = try await dbQueue.read { db in try Attribution.fetchCount(db) } + print("attributions", count) + } + +}