import Foundation // Quoting matches spreadsheets: tabs/newlines/quotes round-trip as cell content. enum TSV { static func encode(_ rows: [[String]]) -> String { rows.map { $0.map { text in text.contains(where: { $0 == "\t" || $0 == "\r" || $0 == "\n" || $0 == "\r\n" || $0 == "\"" }) ? "\"" + text.replacingOccurrences(of: "\"", with: "\"\"") + "\"" : text }.joined(separator: "\t") }.joined(separator: "\n") } static func decode(_ text: String) -> [[String]] { let chars = Array(text) var rows: [[String]] = [] var row: [String] = [] var cell = "" var quoted = false var i = 0 while i < chars.count { let ch = chars[i] if ch == "\"" { if quoted && i + 1 < chars.count && chars[i + 1] == "\"" { cell.append(ch) i += 1 } else if quoted || cell.isEmpty { quoted.toggle() } else { cell.append(ch) } } else if !quoted && ch == "\t" { row.append(cell) cell = "" } else if !quoted && (ch == "\n" || ch == "\r\n" || ch == "\r") { row.append(cell) rows.append(row) row = [] cell = "" } else { cell.append(ch) } i += 1 } row.append(cell) rows.append(row) if rows.last == [""] && (text.last == "\n" || text.last == "\r\n" || text.last == "\r") { rows.removeLast() } return rows } }