Browse Source

Remove SwiftCSV

pull/349/head
Niels Andriesse 2 years ago
parent
commit
16bb12e62c
  1. 36
      Session.xcodeproj/project.pbxproj
  2. 133
      Session/Dependencies/SwiftCSV/CSV.swift
  3. 30
      Session/Dependencies/SwiftCSV/Description.swift
  4. 43
      Session/Dependencies/SwiftCSV/EnumeratedView.swift
  5. 32
      Session/Dependencies/SwiftCSV/NamedView.swift
  6. 111
      Session/Dependencies/SwiftCSV/Parser.swift
  7. 106
      Session/Dependencies/SwiftCSV/ParsingState.swift
  8. 23
      Session/Dependencies/SwiftCSV/String+Lines.swift

36
Session.xcodeproj/project.pbxproj

@ -682,13 +682,6 @@
C38EF40B255B6DF7007E1867 /* TappableStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3ED255B6DF6007E1867 /* TappableStackView.swift */; };
C38EF40C255B6DF7007E1867 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF3EE255B6DF6007E1867 /* GradientView.swift */; };
C38EF48A255B7E3F007E1867 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; };
C396DAEF2518408B00FF6DC5 /* ParsingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAE82518408900FF6DC5 /* ParsingState.swift */; };
C396DAF02518408B00FF6DC5 /* String+Lines.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAE92518408A00FF6DC5 /* String+Lines.swift */; };
C396DAF12518408B00FF6DC5 /* EnumeratedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAEA2518408A00FF6DC5 /* EnumeratedView.swift */; };
C396DAF22518408B00FF6DC5 /* NamedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAEB2518408A00FF6DC5 /* NamedView.swift */; };
C396DAF32518408B00FF6DC5 /* Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAEC2518408A00FF6DC5 /* Description.swift */; };
C396DAF42518408B00FF6DC5 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAED2518408B00FF6DC5 /* Parser.swift */; };
C396DAF52518408B00FF6DC5 /* CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = C396DAEE2518408B00FF6DC5 /* CSV.swift */; };
C3A3A08F256E1728004D228D /* FullTextSearchFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB7F255A581100E217F9 /* FullTextSearchFinder.swift */; };
C3A3A0EC256E1949004D228D /* OWSRecipientIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBEC255A581B00E217F9 /* OWSRecipientIdentity.m */; };
C3A3A0F5256E194C004D228D /* OWSRecipientIdentity.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAA0255A57FF00E217F9 /* OWSRecipientIdentity.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -1762,13 +1755,6 @@
C396469D2509D3F400B0B9F5 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
C396469E2509D40400B0B9F5 /* vi-VN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "vi-VN"; path = "vi-VN.lproj/Localizable.strings"; sourceTree = "<group>"; };
C396469F2509D41100B0B9F5 /* id-ID */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "id-ID"; path = "id-ID.lproj/Localizable.strings"; sourceTree = "<group>"; };
C396DAE82518408900FF6DC5 /* ParsingState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParsingState.swift; sourceTree = "<group>"; };
C396DAE92518408A00FF6DC5 /* String+Lines.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Lines.swift"; sourceTree = "<group>"; };
C396DAEA2518408A00FF6DC5 /* EnumeratedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumeratedView.swift; sourceTree = "<group>"; };
C396DAEB2518408A00FF6DC5 /* NamedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NamedView.swift; sourceTree = "<group>"; };
C396DAEC2518408A00FF6DC5 /* Description.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Description.swift; sourceTree = "<group>"; };
C396DAED2518408B00FF6DC5 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
C396DAEE2518408B00FF6DC5 /* CSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSV.swift; sourceTree = "<group>"; };
C39DD28724F3318C008590FC /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
C3A3A170256E1D25004D228D /* SSKReachabilityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKReachabilityManager.swift; sourceTree = "<group>"; };
C3A71D0A2558989C0043A11F /* MessageWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageWrapper.swift; sourceTree = "<group>"; };
@ -2483,7 +2469,6 @@
isa = PBXGroup;
children = (
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */,
C396DAE72518407300FF6DC5 /* SwiftCSV */,
);
path = Dependencies;
sourceTree = "<group>";
@ -3216,20 +3201,6 @@
path = Database;
sourceTree = "<group>";
};
C396DAE72518407300FF6DC5 /* SwiftCSV */ = {
isa = PBXGroup;
children = (
C396DAEE2518408B00FF6DC5 /* CSV.swift */,
C396DAEC2518408A00FF6DC5 /* Description.swift */,
C396DAEA2518408A00FF6DC5 /* EnumeratedView.swift */,
C396DAEB2518408A00FF6DC5 /* NamedView.swift */,
C396DAED2518408B00FF6DC5 /* Parser.swift */,
C396DAE82518408900FF6DC5 /* ParsingState.swift */,
C396DAE92518408A00FF6DC5 /* String+Lines.swift */,
);
path = SwiftCSV;
sourceTree = "<group>";
};
C3A721332558BDDF0043A11F /* Open Groups */ = {
isa = PBXGroup;
children = (
@ -5032,9 +5003,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C396DAF52518408B00FF6DC5 /* CSV.swift in Sources */,
B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */,
C396DAF12518408B00FF6DC5 /* EnumeratedView.swift in Sources */,
34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */,
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */,
34DBF007206C3CB200025978 /* OWSBubbleShapeView.m in Sources */,
@ -5063,7 +5032,6 @@
34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */,
EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */,
45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */,
C396DAF32518408B00FF6DC5 /* Description.swift in Sources */,
34129B8621EF877A005457A8 /* LinkPreviewView.swift in Sources */,
34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */,
451166C01FD86B98000739BA /* AccountManager.swift in Sources */,
@ -5079,7 +5047,6 @@
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */,
34A8B3512190A40E00218A25 /* MediaAlbumCellView.swift in Sources */,
34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */,
C396DAF42518408B00FF6DC5 /* Parser.swift in Sources */,
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */,
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */,
B85A68B12587141A008CC492 /* Storage+Resetting.swift in Sources */,
@ -5129,7 +5096,6 @@
45F32C222057297A00A300D5 /* MediaDetailViewController.m in Sources */,
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */,
B8B26C8F234D629C004ED98C /* MentionCandidateSelectionView.swift in Sources */,
C396DAF02518408B00FF6DC5 /* String+Lines.swift in Sources */,
B8CCF63F23975CFB0091D419 /* JoinPublicChatVC.swift in Sources */,
34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */,
34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */,
@ -5143,7 +5109,6 @@
3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */,
45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */,
4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */,
C396DAF22518408B00FF6DC5 /* NamedView.swift in Sources */,
45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */,
B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */,
34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */,
@ -5157,7 +5122,6 @@
3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */,
45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */,
B8A14D702589CE9000E70D57 /* KeyPairMigrationSuccessSheet.swift in Sources */,
C396DAEF2518408B00FF6DC5 /* ParsingState.swift in Sources */,
3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */,
34D1F0841F8678AA0066283D /* ConversationInputToolbar.m in Sources */,
457F671B20746193000EABCD /* QuotedReplyPreview.swift in Sources */,

133
Session/Dependencies/SwiftCSV/CSV.swift

@ -1,133 +0,0 @@
//
// CSV.swift
// SwiftCSV
//
// Created by Naoto Kaneko on 2/18/16.
// Copyright © 2016 Naoto Kaneko. All rights reserved.
//
import Foundation
public protocol View {
associatedtype Rows
associatedtype Columns
var rows: Rows { get }
var columns: Columns { get }
init(header: [String], text: String, delimiter: Character, limitTo: Int?, loadColumns: Bool) throws
}
open class CSV {
static public let comma: Character = ","
public let header: [String]
lazy var _namedView: NamedView = {
return try! NamedView(
header: self.header,
text: self.text,
delimiter: self.delimiter,
loadColumns: self.loadColumns)
}()
lazy var _enumeratedView: EnumeratedView = {
return try! EnumeratedView(
header: self.header,
text: self.text,
delimiter: self.delimiter,
loadColumns: self.loadColumns)
}()
var text: String
var delimiter: Character
let loadColumns: Bool
/// List of dictionaries that contains the CSV data
public var namedRows: [[String : String]] {
return _namedView.rows
}
/// Dictionary of header name to list of values in that column
/// Will not be loaded if loadColumns in init is false
public var namedColumns: [String : [String]] {
return _namedView.columns
}
/// Collection of column fields that contain the CSV data
public var enumeratedRows: [[String]] {
return _enumeratedView.rows
}
/// Collection of columns with metadata.
/// Will not be loaded if loadColumns in init is false
public var enumeratedColumns: [EnumeratedView.Column] {
return _enumeratedView.columns
}
@available(*, unavailable, renamed: "namedRows")
public var rows: [[String : String]] {
return namedRows
}
@available(*, unavailable, renamed: "namedColumns")
public var columns: [String : [String]] {
return namedColumns
}
/// Load CSV data from a string.
///
/// - parameter string: CSV contents to parse.
/// - parameter delimiter: Character used to separate row and header fields (default is ',')
/// - parameter loadColumns: Whether to populate the `columns` dictionary (default is `true`)
/// - throws: `CSVParseError` when parsing `string` fails.
public init(string: String, delimiter: Character = comma, loadColumns: Bool = true) throws {
self.text = string
self.delimiter = delimiter
self.loadColumns = loadColumns
self.header = try Parser.array(text: string, delimiter: delimiter, limitTo: 1).first ?? []
}
@available(*, deprecated, message: "Use init(url:delimiter:encoding:loadColumns:) instead of this path-based approach. Also, calling the parameter `name` instead of `path` was a mistake.")
public convenience init(name: String, delimiter: Character = comma, encoding: String.Encoding = .utf8, loadColumns: Bool = true) throws {
try self.init(url: URL(fileURLWithPath: name), delimiter: delimiter, encoding: encoding, loadColumns: loadColumns)
}
/// Load a CSV file as a named resource from `bundle`.
///
/// - parameter name: Name of the file resource inside `bundle`.
/// - parameter ext: File extension of the resource; use `nil` to load the first file matching the name (default is `nil`)
/// - parameter bundle: `Bundle` to use for resource lookup (default is `.main`)
/// - parameter delimiter: Character used to separate row and header fields (default is ',')
/// - parameter encoding: encoding used to read file (default is `.utf8`)
/// - parameter loadColumns: Whether to populate the columns dictionary (default is `true`)
/// - throws: `CSVParseError` when parsing the contents of the resource fails, or file loading errors.
/// - returns: `nil` if the resource could not be found
public convenience init?(name: String, extension ext: String? = nil, bundle: Bundle = .main, delimiter: Character = comma, encoding: String.Encoding = .utf8, loadColumns: Bool = true) throws {
guard let url = bundle.url(forResource: name, withExtension: ext) else {
return nil
}
try self.init(url: url, delimiter: delimiter, encoding: encoding, loadColumns: loadColumns)
}
/// Load a CSV file from `url`.
///
/// - parameter url: URL of the file (will be passed to `String(contentsOfURL:encoding:)` to load)
/// - parameter delimiter: Character used to separate row and header fields (default is ',')
/// - parameter encoding: Character encoding to read file (default is `.utf8`)
/// - parameter loadColumns: Whether to populate the columns dictionary (default is `true`)
/// - throws: `CSVParseError` when parsing the contents of `url` fails, or file loading errors.
public convenience init(url: URL, delimiter: Character = comma, encoding: String.Encoding = .utf8, loadColumns: Bool = true) throws {
let contents = try String(contentsOf: url, encoding: encoding)
try self.init(string: contents, delimiter: delimiter, loadColumns: loadColumns)
}
/// Turn the CSV data into NSData using a given encoding
open func dataUsingEncoding(_ encoding: String.Encoding) -> Data? {
return description.data(using: encoding)
}
}

30
Session/Dependencies/SwiftCSV/Description.swift

@ -1,30 +0,0 @@
//
// Description.swift
// SwiftCSV
//
// Created by Will Richardson on 11/04/16.
// Copyright © 2016 Naoto Kaneko. All rights reserved.
//
import Foundation
extension CSV: CustomStringConvertible {
public var description: String {
let head = header.joined(separator: ",") + "\n"
let cont = namedRows.map { row in
return header.map { key -> String in
let value = row[key]!
// Add quotes if value contains a comma
if value.contains(",") {
return "\"\(value)\""
}
return value
}.joined(separator: ",")
}.joined(separator: "\n")
return head + cont
}
}

43
Session/Dependencies/SwiftCSV/EnumeratedView.swift

@ -1,43 +0,0 @@
//
// EnumeratedView.swift
// SwiftCSV
//
// Created by Christian Tietze on 25/10/16.
// Copyright © 2016 Naoto Kaneko. All rights reserved.
//
import Foundation
public struct EnumeratedView: View {
public struct Column {
public let header: String
public let rows: [String]
}
public private(set) var rows: [[String]]
public private(set) var columns: [Column]
public init(header: [String], text: String, delimiter: Character, limitTo: Int? = nil, loadColumns: Bool = false) throws {
var rows = [[String]]()
var columns: [EnumeratedView.Column] = []
try Parser.enumerateAsArray(text: text, delimiter: delimiter, limitTo: limitTo, startAt: 1) { fields in
rows.append(fields)
}
if loadColumns {
columns = header.enumerated().map { (index: Int, header: String) -> EnumeratedView.Column in
return EnumeratedView.Column(
header: header,
rows: rows.map { $0[index] })
}
}
self.rows = rows
self.columns = columns
}
}

32
Session/Dependencies/SwiftCSV/NamedView.swift

@ -1,32 +0,0 @@
//
// NamedView.swift
// SwiftCSV
//
// Created by Christian Tietze on 22/10/16.
// Copyright © 2016 Naoto Kaneko. All rights reserved.
//
public struct NamedView: View {
public var rows: [[String : String]]
public var columns: [String : [String]]
public init(header: [String], text: String, delimiter: Character, limitTo: Int? = nil, loadColumns: Bool = false) throws {
var rows = [[String: String]]()
var columns = [String: [String]]()
try Parser.enumerateAsDict(header: header, content: text, delimiter: delimiter, limitTo: limitTo) { dict in
rows.append(dict)
}
if loadColumns {
for field in header {
columns[field] = rows.map { $0[field] ?? "" }
}
}
self.rows = rows
self.columns = columns
}
}

111
Session/Dependencies/SwiftCSV/Parser.swift

@ -1,111 +0,0 @@
//
// Parser.swift
// SwiftCSV
//
// Created by Will Richardson on 13/04/16.
// Copyright © 2016 Naoto Kaneko. All rights reserved.
//
extension CSV {
/// Parse the file and call a block on each row, passing it in as a list of fields
/// limitTo will limit the result to a certain number of lines
public func enumerateAsArray(limitTo: Int? = nil, startAt: Int = 0, _ block: @escaping ([String]) -> ()) throws {
try Parser.enumerateAsArray(text: self.text, delimiter: self.delimiter, limitTo: limitTo, startAt: startAt, block: block)
}
public func enumerateAsDict(_ block: @escaping ([String : String]) -> ()) throws {
try Parser.enumerateAsDict(header: self.header, content: self.text, delimiter: self.delimiter, block: block)
}
}
enum Parser {
static func array(text: String, delimiter: Character, limitTo: Int? = nil, startAt: Int = 0) throws -> [[String]] {
var rows = [[String]]()
try enumerateAsArray(text: text, delimiter: delimiter) { row in
rows.append(row)
}
return rows
}
/// Parse the text and call a block on each row, passing it in as a list of fields.
///
/// - parameter text: Text to parse.
/// - parameter delimiter: Character to split row and header fields by (default is ',')
/// - parameter limitTo: If set to non-nil value, enumeration stops
/// at the row with index `limitTo` (or on end-of-text, whichever is earlier.
/// - parameter startAt: Offset of rows to ignore before invoking `block` for the first time. Default is 0.
/// - parameter block: Callback invoked for every parsed row between `startAt` and `limitTo` in `text`.
static func enumerateAsArray(text: String, delimiter: Character, limitTo: Int? = nil, startAt: Int = 0, block: @escaping ([String]) -> ()) throws {
var currentIndex = text.startIndex
let endIndex = text.endIndex
var fields = [String]()
var field = ""
var count = 0
func finishRow() {
fields.append(String(field))
if count >= startAt {
block(fields)
}
count += 1
fields = [String]()
field = ""
}
var state: ParsingState = ParsingState(
delimiter: delimiter,
finishRow: finishRow,
appendChar: { field.append($0) },
finishField: {
fields.append(field)
field = ""
})
func limitReached(_ count: Int) -> Bool {
guard let limitTo = limitTo,
count >= limitTo
else { return false }
return true
}
while currentIndex < endIndex {
let char = text[currentIndex]
try state.change(char)
if limitReached(count) {
break
}
currentIndex = text.index(after: currentIndex)
}
if !fields.isEmpty || !field.isEmpty || limitReached(count) {
fields.append(field)
block(fields)
}
}
static func enumerateAsDict(header: [String], content: String, delimiter: Character, limitTo: Int? = nil, block: @escaping ([String : String]) -> ()) throws {
let enumeratedHeader = header.enumerated()
try enumerateAsArray(text: content, delimiter: delimiter, startAt: 1) { fields in
var dict = [String: String]()
for (index, head) in enumeratedHeader {
dict[head] = index < fields.count ? fields[index] : ""
}
block(dict)
}
}
}

106
Session/Dependencies/SwiftCSV/ParsingState.swift

@ -1,106 +0,0 @@
//
// ParsingState.swift
// SwiftCSV
//
// Created by Christian Tietze on 25/10/16.
// Copyright © 2016 Naoto Kaneko. All rights reserved.
//
public enum CSVParseError: Error {
case generic(message: String)
case quotation(message: String)
}
/// State machine of parsing CSV contents character by character.
struct ParsingState {
private(set) var atStart = true
private(set) var parsingField = false
private(set) var parsingQuotes = false
private(set) var innerQuotes = false
let delimiter: Character
let finishRow: () -> Void
let appendChar: (Character) -> Void
let finishField: () -> Void
init(delimiter: Character,
finishRow: @escaping () -> Void,
appendChar: @escaping (Character) -> Void,
finishField: @escaping () -> Void) {
self.delimiter = delimiter
self.finishRow = finishRow
self.appendChar = appendChar
self.finishField = finishField
}
mutating func change(_ char: Character) throws {
if atStart {
if char == "\"" {
atStart = false
parsingQuotes = true
} else if char == delimiter {
finishField()
} else if char.isNewline {
finishRow()
} else {
parsingField = true
atStart = false
appendChar(char)
}
} else if parsingField {
if innerQuotes {
if char == "\"" {
appendChar(char)
innerQuotes = false
} else {
throw CSVParseError.quotation(message: "Can't have non-quote here: \(char)")
}
} else {
if char == "\"" {
innerQuotes = true
} else if char == delimiter {
atStart = true
parsingField = false
innerQuotes = false
finishField()
} else if char.isNewline {
atStart = true
parsingField = false
innerQuotes = false
finishRow()
} else {
appendChar(char)
}
}
} else if parsingQuotes {
if innerQuotes {
if char == "\"" {
appendChar(char)
innerQuotes = false
} else if char == delimiter {
atStart = true
parsingField = false
innerQuotes = false
finishField()
} else if char.isNewline {
atStart = true
parsingQuotes = false
innerQuotes = false
finishRow()
} else {
throw CSVParseError.quotation(message: "Can't have non-quote here: \(char)")
}
} else {
if char == "\"" {
innerQuotes = true
} else {
appendChar(char)
}
}
} else {
throw CSVParseError.generic(message: "me_irl")
}
}
}

23
Session/Dependencies/SwiftCSV/String+Lines.swift

@ -1,23 +0,0 @@
//
// String+Lines.swift
// SwiftCSV
//
// Created by Naoto Kaneko on 2/24/16.
// Copyright © 2016 Naoto Kaneko. All rights reserved.
//
extension String {
internal var firstLine: String {
var current = startIndex
while current < endIndex && self[current].isNewline == false {
current = self.index(after: current)
}
return String(self[..<current])
}
}
extension Character {
internal var isNewline: Bool {
return self == "\n" || self == "\r\n" || self == "\r"
}
}
Loading…
Cancel
Save