From 872dc52f5742c9b0a3d4c82bfd3a009b29a7cec8 Mon Sep 17 00:00:00 2001 From: Sergey Khruschak Date: Fri, 30 Jan 2026 17:46:44 +0200 Subject: [PATCH 1/2] Remove a single column --- Sources/table/Header.swift | 12 +++++ Sources/table/MainApp.swift | 9 ++++ Sources/table/TableView.swift | 46 ++++++++++++++++--- ...> ColumnsManipulationTableViewTests.swift} | 25 +++++++++- 4 files changed, 85 insertions(+), 7 deletions(-) rename Tests/table-Tests/{NewColumnsTableViewTests.swift => ColumnsManipulationTableViewTests.swift} (93%) diff --git a/Sources/table/Header.swift b/Sources/table/Header.swift index 1ea8539..d8b2a46 100644 --- a/Sources/table/Header.swift +++ b/Sources/table/Header.swift @@ -42,6 +42,18 @@ class Header { cols.firstIndex(of: ofColumn) } + func filter(indexes: Set) -> Header { + let filteredCols = cols.enumerated().filter { index, col in + indexes.contains(index) + }.map { $0.element } + + let filteredTypes = cols.enumerated().filter { index, col in + indexes.contains(index) + }.map { $0.offset < types.count ? types[$0.offset] : .string } + + return Header(components: filteredCols, types: filteredTypes) + } + func type(ofColumn: String) -> CellType? { guard let index = index(ofColumn: ofColumn) else { return .string } return index < types.count ? types[index] : .string diff --git a/Sources/table/MainApp.swift b/Sources/table/MainApp.swift index a937d6e..dca0554 100644 --- a/Sources/table/MainApp.swift +++ b/Sources/table/MainApp.swift @@ -129,6 +129,9 @@ struct MainApp: AsyncParsableCommand { ) var addColumns: [String] = [] + @Option(name: .customLong("remove"), help: "Removes specified columns from the output. Example: --remove password,token.") + var removeColumns: [String] = [] + @Option(name: .customLong("distinct"), help: "Returns only distinct values for the specified column set. Example: --distinct name,city_id.") var distinctColumns: [String] = [] @@ -248,6 +251,12 @@ struct MainApp: AsyncParsableCommand { table = SampledTableView(table: table, percentage: sample) } + if !removeColumns.isEmpty { + debug("Removing columns: \(removeColumns.joined(separator: ","))") + try removeColumns.forEach { if table.header.index(ofColumn: $0) == nil { throw RuntimeError("Column \($0) in remove clause is not found in the table") } } + table = HideColumnsTableView(table: table, hideColumns: removeColumns) + } + let printer = try buildPrinter(formatOpt: formatOpt, outFileFmt: try FileType.outFormat(strFormat: asFormat), outputFile: outputFile) // when print format is set, header is not relevant anymore diff --git a/Sources/table/TableView.swift b/Sources/table/TableView.swift index 0831e8a..6e37783 100644 --- a/Sources/table/TableView.swift +++ b/Sources/table/TableView.swift @@ -37,16 +37,16 @@ class JoinTableView: Table { class NewColumnsTableView: Table { var table: any Table let additionalColumns: [(String, Format)] - - var header: Header { - get { - return self.table.header + Header(components: additionalColumns.map { $0.0 }, types: additionalColumns.map { _ in CellType.string }) - } - } + let header: Header init(table: any Table, additionalColumns: [(String, Format)]) { self.table = table self.additionalColumns = additionalColumns + self.header = self.table.header + + Header( + components: additionalColumns.map { $0.0 }, + types: additionalColumns.map { _ in CellType.string } + ) } func next() throws -> Row? { @@ -68,6 +68,40 @@ class NewColumnsTableView: Table { } } +/** Table view with additional dynamic columns */ +class HideColumnsTableView: Table { + var table: any Table + let hideColumns: [String] + let hidenIndexes: [Int] + let header: Header + + init(table: any Table, hideColumns: [String]) { + self.table = table + self.hideColumns = hideColumns + self.hidenIndexes = hideColumns.compactMap { col in + table.header.index(ofColumn: col) + } + + self.header = table.header.filter(indexes: Set(0.. Row? { + let row = try table.next() + + if let row { + return Row( + header: header, + index: row.index, + cells: row.components.enumerated().filter { index, cell in + !hidenIndexes.contains(index) + }.map { $0.element } + ) + } else { + return nil + } + } +} + /** Table view with filtered columns */ class ColumnsTableView: Table { var table: any Table diff --git a/Tests/table-Tests/NewColumnsTableViewTests.swift b/Tests/table-Tests/ColumnsManipulationTableViewTests.swift similarity index 93% rename from Tests/table-Tests/NewColumnsTableViewTests.swift rename to Tests/table-Tests/ColumnsManipulationTableViewTests.swift index adb612c..d8eb31f 100644 --- a/Tests/table-Tests/NewColumnsTableViewTests.swift +++ b/Tests/table-Tests/ColumnsManipulationTableViewTests.swift @@ -1,7 +1,7 @@ import XCTest @testable import table -class NewColumnsTableViewTests: XCTestCase { +class ColumnsManipulationTableViewTests: XCTestCase { func testAddSingleStaticColumn() throws { let table = ParsedTable.fromArray([ @@ -330,5 +330,28 @@ class NewColumnsTableViewTests: XCTestCase { XCTAssertNil(try newColumnsTable.next()) } + + func testRemovingColumns() throws { + let table = ParsedTable.fromArray([ + ["Alice", "30", "Engineer"], + ["Bob", "25", "Designer"] + ], header: ["name", "age", "profession"]) + + let hideColumnsTable = HideColumnsTableView( + table: table, + hideColumns: ["age"] + ) + + XCTAssertEqual(hideColumnsTable.header.columnsStr(), "name,profession") + let row1 = try hideColumnsTable.next()! + XCTAssertEqual(row1["name"], "Alice") + XCTAssertEqual(row1["profession"], "Engineer") + XCTAssertNil(row1["age"]) + + let row2 = try hideColumnsTable.next()! + XCTAssertEqual(row2["name"], "Bob") + XCTAssertEqual(row2["profession"], "Designer") + XCTAssertNil(row2["age"]) + } } From 41fd767a692fea8541ebfe44bfdc59a5c5114707 Mon Sep 17 00:00:00 2001 From: Sergey Khruschak Date: Fri, 30 Jan 2026 18:23:47 +0200 Subject: [PATCH 2/2] linux static linking --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 101d7e0..678cc0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: run: bash -c 'echo "let appVersion = \"`git describe --abbrev=0 --tags` (`date +%F`)\""' > ./Sources/table/Version.swift - name: Build Linux if: ${{ matrix.os == 'ubuntu-latest' }} - run: swift build -c release + run: swift build -c release --static-swift-stdlib - name: Rename file if: ${{ matrix.os == 'ubuntu-latest' }} run: bash -c 'mv .build/release/table ./table'