diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5655a49..bf78d14 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,19 +1,9 @@ { "name": "Swift", - "image": "swift:5.9", + "image": "swift:6.1", "features": { - "ghcr.io/devcontainers/features/common-utils:2": { - "installZsh": "false", - "username": "vscode", - "userUid": "1000", - "userGid": "1000", - "upgradePackages": "false" - }, - "ghcr.io/devcontainers/features/git:1": { - "version": "os-provided", - "ppa": "false" - }, - "ghcr.io/swift-server-community/swift-devcontainer-features/swift-format:0": {} + "ghcr.io/devcontainers/features/common-utils:2": {}, + "ghcr.io/devcontainers/features/git:1": {} }, "runArgs": [ "--cap-add=SYS_PTRACE", @@ -30,16 +20,13 @@ }, // Add the IDs of extensions you want installed when the container is created. "extensions": [ - "sswg.swift-lang" + "swiftlang.swift-vscode" ] } }, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "swift --version", - // Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "vscode" + "remoteUser": "root" } diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index e355357..d4db5ef 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - swift: ["5.10"] + swift: ["6.1"] steps: - uses: actions/checkout@v4 - uses: swift-actions/setup-swift@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96f7c8f..101d7e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest ] - swift: ["5.10"] + swift: ["6.1"] steps: - uses: actions/checkout@v4 - uses: swift-actions/setup-swift@v2 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d0b3b2a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "type": "swift", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:table-cli}", + "name": "Debug table", + "program": "${workspaceFolder:table-cli}/.build/debug/table", + "preLaunchTask": "swift: Build Debug table" + }, + { + "type": "swift", + "request": "launch", + "args": [], + "cwd": "${workspaceFolder:table-cli}", + "name": "Release table", + "program": "${workspaceFolder:table-cli}/.build/release/table", + "preLaunchTask": "swift: Build Release table" + } + ] +} \ No newline at end of file diff --git a/Package.resolved b/Package.resolved index aceaff7..947204d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,14 +1,15 @@ { + "originHash" : "84a9b1698a9c385c559d9f7dafb10494b6e8006ab6e2051965b29e02f3f15759", "pins" : [ { "identity" : "swift-argument-parser", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser.git", "state" : { - "revision" : "011f0c765fb46d9cac61bca19be0527e99c98c8b", - "version" : "1.5.1" + "revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3", + "version" : "1.6.1" } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index b0188bd..b0b30f2 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.8 +// swift-tools-version: 6.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,8 +6,7 @@ import PackageDescription let package = Package( name: "table", dependencies: [ - .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.1"), - // .package(url: "https://github.com/groue/GRMustache.swift", from: "4.0.0") + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.6.1") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -16,8 +15,8 @@ let package = Package( name: "table", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), - // .product(name: "Mustache", package: "GRMustache.swift") - ]), + ] + ), .testTarget( name: "table-Tests", dependencies: ["table"]), diff --git a/Sources/table/Extensions.swift b/Sources/table/Extensions.swift index 556d19b..caa55ca 100644 --- a/Sources/table/Extensions.swift +++ b/Sources/table/Extensions.swift @@ -56,7 +56,5 @@ extension Table { } func debug(_ message: String) { - if Global.debug { - print(message) - } + Debug.debug(message) } \ No newline at end of file diff --git a/Sources/table/MainApp.swift b/Sources/table/MainApp.swift index 4655411..8c81e7d 100644 --- a/Sources/table/MainApp.swift +++ b/Sources/table/MainApp.swift @@ -1,8 +1,24 @@ import ArgumentParser import Foundation -struct Global { - static var debug: Bool = false +struct Debug { + // this one is set on start, so we don't care about concurrency checks + nonisolated(unsafe) private static var debug: Bool = false + + static func enableDebug() { + debug = true + print("Debug mode enabled") + } + + static func debug(_ message: String) { + if debug { + print(message) + } + } + + static func isDebugEnabled() -> Bool { + return debug + } } func buildPrinter(formatOpt: Format?, outFileFmt: FileType, outputFile: String?) throws -> TablePrinter { @@ -31,7 +47,7 @@ func buildPrinter(formatOpt: Format?, outFileFmt: FileType, outputFile: String?) } @main -struct MainApp: ParsableCommand { +struct MainApp: ParsableCommand { static let configuration = CommandConfiguration( commandName: "table", abstract: "A utility for transforming CSV files of SQL output.", @@ -112,8 +128,7 @@ struct MainApp: ParsableCommand { mutating func run() throws { if debugEnabled { - Global.debug = true - print("Debug enabled") + Debug.enableDebug() } let userTypes = try columnTypes.map { try CellType.fromStringList($0) } @@ -165,7 +180,7 @@ struct MainApp: ParsableCommand { let formatOpt = try printFormat.map { try Format(format: $0).validated(header: table.header) } if let sortColumns { - let expression = try Sort(sortColumns).validated(header: table.header) + let expression = try Sort(sortColumns).validated(header: table.header) debug("Sorting by columns: \(expression.columns.map { (name, order) in "\(name) \(order)" }.joined(separator: ","))") table = try InMemoryTableView(table: table).sort(expr: expression) } diff --git a/Sources/table/Shell.swift b/Sources/table/Shell.swift index ea9129a..fdc68e8 100644 --- a/Sources/table/Shell.swift +++ b/Sources/table/Shell.swift @@ -11,9 +11,7 @@ func shell(_ command: String, env: Dictionary = [:]) throws -> S task.arguments = ["-c", command] task.launchPath = "/bin/bash" task.standardInput = nil - - - try task.run() + task.launch() task.waitUntilExit() let data = pipe.fileHandleForReading.readDataToEndOfFile() diff --git a/Sources/table/Table.swift b/Sources/table/Table.swift index f3529d2..4487948 100644 --- a/Sources/table/Table.swift +++ b/Sources/table/Table.swift @@ -96,7 +96,7 @@ class ParsedTable: Table { file = try FileHandle(forReadingAtPath: path).orThrow(RuntimeError("File \(path) is not found")) } else { - if (isatty(fileno(stdin)) != 0) { + if (isatty(STDIN_FILENO) != 0) { throw RuntimeError("No input file provided and standard input is not a terminal. Use --input to specify a file or --generate to generate rows.") } diff --git a/test-data/mixed_quotes_sql.out b/test-data/mixed_quotes_sql.out new file mode 100644 index 0000000..c5e72a3 --- /dev/null +++ b/test-data/mixed_quotes_sql.out @@ -0,0 +1,13 @@ ++-----------------------------------+---------+--------------------+ +| ref | count | id | ++-----------------------------------+---------+--------------------+ +| 45af1d3287f11111 | 5 | 50403400 | +| 68ce1086965bd401 | 2 | 3A | +| 546b1729e5ae8401 | 14 | 241.39564439115765 | +| 0407549437d54cb5b1dbea45c03e2a1c | 10 | 1A01 | +| 0864c5bdf9604a8ebf1c5816aff5087f | 8 | 1811 | +| 0ffd3d159c724129932d90ed315fb5d6 | 3 | 900 | +| 165b6f71e80348d49c3dc236c3ffe373 | 5 | 1500 | +| 1a4bf1fb1ebb46818a41e0e208a14730 | 2 | 80 | +| 2a2e17c3d1004ad5b0489ec85b28396a | 5 | 1500 | ++-----------------------------------+---------+--------------------+ \ No newline at end of file