mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
3.2 KiB
Swift
107 lines
3.2 KiB
Swift
5 years ago
|
//
|
||
|
// 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")
|
||
|
}
|
||
|
}
|
||
|
}
|