Skip to content

Commit

Permalink
Add and use SchemaOptions (#569)
Browse files Browse the repository at this point in the history
- The backup & batch programs still have a lax/strict switch. However
all the tables now use the option set to set how strict they are.
- They default to .strict since the programs did.
- Next step will be to add control for each table to the programs.
- This reverts #532.
  • Loading branch information
bolsinga authored Dec 26, 2024
1 parent b035874 commit 74a7fa7
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 28 deletions.
4 changes: 2 additions & 2 deletions Sources/iTunes/Array+DB.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import Foundation

extension Array where Element == Track {
func database(
storage: DatabaseStorage, loggingToken: String?, schemaConstrainsts: SchemaConstraints
storage: DatabaseStorage, loggingToken: String?, schemaOptions: SchemaOptions
) async throws -> Data {
let dbEncoder = try TracksDBEncoder(
storage: storage, rowEncoder: self.rowEncoder(loggingToken), loggingToken: loggingToken)
do {
try await dbEncoder.encode(schemaConstrainsts: schemaConstrainsts)
try await dbEncoder.encode(schemaOptions: schemaOptions)
let data = try await dbEncoder.data()
await dbEncoder.close()
return data
Expand Down
4 changes: 2 additions & 2 deletions Sources/iTunes/Array+TracksSqlData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import Foundation

extension Array where Element == Track {
func sqlData(loggingToken: String?, schemaConstraints: SchemaConstraints) throws -> Data {
func sqlData(loggingToken: String?, schemaOptions: SchemaOptions) throws -> Data {
let encoder = TracksSQLSourceEncoder()
return try encoder.encode(
self, loggingToken: loggingToken, schemaConstraints: schemaConstraints)
self, loggingToken: loggingToken, schemaOptions: schemaOptions)
}
}
2 changes: 1 addition & 1 deletion Sources/iTunes/BackupCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public struct BackupCommand: AsyncParsableCommand {

try await destination.context(outputFile: outputFile).emit(
tracks, branch: "main", tagPrefix: tagPrefix, version: Self.configuration.version,
schemaConstraints: schemaConstraints)
schemaOptions: schemaConstraints.schemaOptions)
}

public init() {} // This is public and empty to help the compiler.
Expand Down
4 changes: 2 additions & 2 deletions Sources/iTunes/Batch/Batch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ enum Batch: CaseIterable {
extension Batch {
func build(
_ configuration: GitTagData.Configuration, outputDirectory: URL,
schemaConstraints: SchemaConstraints
schemaOptions: SchemaOptions
) async throws {
var patchedTracksData = try await GitTagData(configuration: configuration)
.transformTaggedTracks {
Expand All @@ -29,7 +29,7 @@ extension Batch {
}()

return try await destination.data(
for: $1, loggingToken: "batch-\($0)", schemaConstraints: schemaConstraints)
for: $1, loggingToken: "batch-\($0)", schemaOptions: schemaOptions)
}

let pathExtension = {
Expand Down
3 changes: 2 additions & 1 deletion Sources/iTunes/Batch/BatchCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ public struct BatchCommand: AsyncParsableCommand {
let configuration = GitTagData.Configuration(
directory: gitDirectory, tagPrefix: tagPrefix, fileName: Self.fileName)
try await batch.build(
configuration, outputDirectory: outputDirectory, schemaConstraints: schemaConstraints)
configuration, outputDirectory: outputDirectory,
schemaOptions: schemaConstraints.schemaOptions)
}

public init() {} // This is public and empty to help the compiler.
Expand Down
6 changes: 3 additions & 3 deletions Sources/iTunes/Destination+Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
import Foundation

extension Destination {
func data(for tracks: [Track], loggingToken: String?, schemaConstraints: SchemaConstraints)
func data(for tracks: [Track], loggingToken: String?, schemaOptions: SchemaOptions)
async throws -> Data
{
switch self {
case .json, .jsonGit:
try tracks.jsonData()
case .sqlCode:
try tracks.sqlData(loggingToken: loggingToken, schemaConstraints: schemaConstraints)
try tracks.sqlData(loggingToken: loggingToken, schemaOptions: schemaOptions)
case .db:
try await tracks.database(
storage: .memory, loggingToken: loggingToken, schemaConstrainsts: schemaConstraints)
storage: .memory, loggingToken: loggingToken, schemaOptions: schemaOptions)
}
}
}
4 changes: 2 additions & 2 deletions Sources/iTunes/Destination+Tracks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ extension Destination {

func emit(
_ tracks: [Track], branch: String, tagPrefix: String, version: String,
schemaConstraints: SchemaConstraints
schemaOptions: SchemaOptions
) async throws {
enum DataExportError: Error {
case noTracks
Expand All @@ -52,7 +52,7 @@ extension Destination {
let tracks = tracks.sorted()

let data = try await self.data(
for: tracks, loggingToken: nil, schemaConstraints: schemaConstraints)
for: tracks, loggingToken: nil, schemaOptions: schemaOptions)

if let outputFile = self.url {
try await self.fileWriter(
Expand Down
17 changes: 17 additions & 0 deletions Sources/iTunes/SchemaConstraints+LaxSchemaOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// SchemaConstraints+SchemaOptions.swift
// itunes_json
//
// Created by Greg Bolsinga on 12/25/24.
//

extension SchemaConstraints {
var schemaOptions: SchemaOptions {
switch self {
case .strict:
.strictSchema
case .lax:
.laxSchema
}
}
}
27 changes: 27 additions & 0 deletions Sources/iTunes/SchemaOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// SchemaOptions.swift
// itunes_json
//
// Created by Greg Bolsinga on 12/25/24.
//

import Foundation

struct SchemaOptions: OptionSet {
let rawValue: UInt

private static let laxArtist = SchemaOptions(rawValue: 1 << 0)
private static let laxAlbum = SchemaOptions(rawValue: 1 << 1)
private static let laxSong = SchemaOptions(rawValue: 1 << 2)
private static let laxPlays = SchemaOptions(rawValue: 1 << 3)

static let strictSchema = SchemaOptions()
static let laxSchema: SchemaOptions = [
Self.laxArtist, Self.laxAlbum, Self.laxSong, Self.laxPlays,
]

var artistConstraints: SchemaConstraints { self.contains(.laxArtist) ? .lax : .strict }
var albumConstraints: SchemaConstraints { self.contains(.laxAlbum) ? .lax : .strict }
var songConstraints: SchemaConstraints { self.contains(.laxSong) ? .lax : .strict }
var playsConstraints: SchemaConstraints { self.contains(.laxPlays) ? .lax : .strict }
}
12 changes: 7 additions & 5 deletions Sources/iTunes/TracksDBEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ struct TracksDBEncoder {
rowEncoder.playTableBuilder(songLookup), schemaConstraints: schemaConstrainsts)
}

func encode(schemaConstrainsts: SchemaConstraints) async throws {
func encode(schemaOptions: SchemaOptions) async throws {
try await db.execute("PRAGMA foreign_keys = ON;")
let artistLookup = try await emitArtists(schemaConstrainsts: .strict)
let albumLookup = try await emitAlbums(schemaConstrainsts: schemaConstrainsts)
let artistLookup = try await emitArtists(schemaConstrainsts: schemaOptions.artistConstraints)
let albumLookup = try await emitAlbums(schemaConstrainsts: schemaOptions.albumConstraints)
let songLookup = try await emitSongs(
artistLookup: artistLookup, albumLookup: albumLookup, schemaConstrainsts: schemaConstrainsts)
try await emitPlays(songLookup: songLookup, schemaConstrainsts: schemaConstrainsts)
artistLookup: artistLookup, albumLookup: albumLookup,
schemaConstrainsts: schemaOptions.songConstraints)
try await emitPlays(
songLookup: songLookup, schemaConstrainsts: schemaOptions.playsConstraints)
try await db.execute(rowEncoder.views)
}

Expand Down
24 changes: 14 additions & 10 deletions Sources/iTunes/TracksSQLSourceEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,42 @@ struct TracksSQLSourceEncoder {
self.rowEncoder = rowEncoder
}

private var tableBuilders: [any TableBuilder] {
private func tableBuilders(schemaOptions: SchemaOptions) -> [(
any TableBuilder, SchemaConstraints
)] {
[
rowEncoder.artistTableBuilder, rowEncoder.albumTableBuilder, rowEncoder.songTableBuilder(),
rowEncoder.playTableBuilder(),
(rowEncoder.artistTableBuilder, schemaOptions.artistConstraints),
(rowEncoder.albumTableBuilder, schemaOptions.albumConstraints),
(rowEncoder.songTableBuilder(), schemaOptions.songConstraints),
(rowEncoder.playTableBuilder(), schemaOptions.playsConstraints),
]
}

fileprivate func sqlStatements(schemaConstraints: SchemaConstraints) -> String {
fileprivate func sqlStatements(schemaOptions: SchemaOptions) -> String {
(["PRAGMA foreign_keys = ON;"]
+ tableBuilders.flatMap {
var statements = [$0.schema(constraints: schemaConstraints)]
+ tableBuilders(schemaOptions: schemaOptions).flatMap {
var statements = [$0.schema(constraints: $1)]
statements.append(contentsOf: $0.statements.map { "\($0)" }.sorted())
return statements
} + [rowEncoder.views].compactMap { $0 }).joined(separator: "\n")
}
}

private func encode(
_ tracks: [Track], loggingToken: String?, schemaConstraints: SchemaConstraints
_ tracks: [Track], loggingToken: String?, schemaOptions: SchemaOptions
) -> String {
let encoder = Encoder(rowEncoder: tracks.rowEncoder(loggingToken))
return encoder.sqlStatements(schemaConstraints: schemaConstraints)
return encoder.sqlStatements(schemaOptions: schemaOptions)
}

func encode(_ tracks: [Track], loggingToken: String?, schemaConstraints: SchemaConstraints) throws
func encode(_ tracks: [Track], loggingToken: String?, schemaOptions: SchemaOptions) throws
-> Data
{
enum TracksSQLSourceEncoderError: Error {
case cannotMakeData
}
guard
let data = encode(tracks, loggingToken: loggingToken, schemaConstraints: schemaConstraints)
let data = encode(tracks, loggingToken: loggingToken, schemaOptions: schemaOptions)
.data(using: .utf8)
else {
throw TracksSQLSourceEncoderError.cannotMakeData
Expand Down

0 comments on commit 74a7fa7

Please sign in to comment.